1 /*
2  * Common hostapd/wpa_supplicant ctrl iface code.
3  * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
4  * Copyright (c) 2015, Qualcomm Atheros, Inc.
5  *
6  * This software may be distributed under the terms of the BSD license.
7  * See README for more details.
8  */
9 
10 #include "utils/includes.h"
11 #include <netdb.h>
12 #include <sys/un.h>
13 
14 #include "utils/common.h"
15 #include "ctrl_iface_common.h"
16 
sockaddr_compare(struct sockaddr_storage * a,socklen_t a_len,struct sockaddr_storage * b,socklen_t b_len)17 static int sockaddr_compare(struct sockaddr_storage *a, socklen_t a_len,
18 			    struct sockaddr_storage *b, socklen_t b_len)
19 {
20 	if (a->ss_family != b->ss_family)
21 		return 1;
22 
23 	switch (a->ss_family) {
24 #ifdef CONFIG_CTRL_IFACE_UDP
25 	case AF_INET:
26 	{
27 		struct sockaddr_in *in_a, *in_b;
28 
29 		in_a = (struct sockaddr_in *) a;
30 		in_b = (struct sockaddr_in *) b;
31 
32 		if (in_a->sin_port != in_b->sin_port)
33 			return 1;
34 		if (in_a->sin_addr.s_addr != in_b->sin_addr.s_addr)
35 			return 1;
36 		break;
37 	}
38 	case AF_INET6:
39 	{
40 		struct sockaddr_in6 *in6_a, *in6_b;
41 
42 		in6_a = (struct sockaddr_in6 *) a;
43 		in6_b = (struct sockaddr_in6 *) b;
44 
45 		if (in6_a->sin6_port != in6_b->sin6_port)
46 			return 1;
47 		if (os_memcmp(&in6_a->sin6_addr, &in6_b->sin6_addr,
48 			      sizeof(in6_a->sin6_addr)) != 0)
49 			return 1;
50 		break;
51 	}
52 #endif /* CONFIG_CTRL_IFACE_UDP */
53 #ifdef CONFIG_CTRL_IFACE_UNIX
54 	case AF_UNIX:
55 	{
56 		struct sockaddr_un *u_a, *u_b;
57 
58 		u_a = (struct sockaddr_un *) a;
59 		u_b = (struct sockaddr_un *) b;
60 
61 		if (a_len != b_len ||
62 		    os_memcmp(u_a->sun_path, u_b->sun_path,
63 			      a_len - offsetof(struct sockaddr_un, sun_path))
64 		    != 0)
65 			return 1;
66 		break;
67 	}
68 #endif /* CONFIG_CTRL_IFACE_UNIX */
69 	default:
70 		return 1;
71 	}
72 
73 	return 0;
74 }
75 
76 
sockaddr_print(int level,const char * msg,struct sockaddr_storage * sock,socklen_t socklen)77 void sockaddr_print(int level, const char *msg, struct sockaddr_storage *sock,
78 		    socklen_t socklen)
79 {
80 	switch (sock->ss_family) {
81 #ifdef CONFIG_CTRL_IFACE_UDP
82 	case AF_INET:
83 	case AF_INET6:
84 	{
85 		char host[NI_MAXHOST] = { 0 };
86 		char service[NI_MAXSERV] = { 0 };
87 
88 		getnameinfo((struct sockaddr *) sock, socklen,
89 			    host, sizeof(host),
90 			    service, sizeof(service),
91 			    NI_NUMERICHOST);
92 
93 		wpa_printf(level, "%s %s:%s", msg, host, service);
94 		break;
95 	}
96 #endif /* CONFIG_CTRL_IFACE_UDP */
97 #ifdef CONFIG_CTRL_IFACE_UNIX
98 	case AF_UNIX:
99 	{
100 		char addr_txt[200];
101 
102 		printf_encode(addr_txt, sizeof(addr_txt),
103 			      (u8 *) ((struct sockaddr_un *) sock)->sun_path,
104 			      socklen - offsetof(struct sockaddr_un, sun_path));
105 		wpa_printf(level, "%s %s", msg, addr_txt);
106 		break;
107 	}
108 #endif /* CONFIG_CTRL_IFACE_UNIX */
109 	default:
110 		wpa_printf(level, "%s", msg);
111 		break;
112 	}
113 }
114 
115 
ctrl_iface_attach(struct dl_list * ctrl_dst,struct sockaddr_storage * from,socklen_t fromlen)116 int ctrl_iface_attach(struct dl_list *ctrl_dst, struct sockaddr_storage *from,
117 		      socklen_t fromlen)
118 {
119 	struct wpa_ctrl_dst *dst;
120 
121 	dst = os_zalloc(sizeof(*dst));
122 	if (dst == NULL)
123 		return -1;
124 	os_memcpy(&dst->addr, from, fromlen);
125 	dst->addrlen = fromlen;
126 	dst->debug_level = MSG_INFO;
127 	dl_list_add(ctrl_dst, &dst->list);
128 
129 	sockaddr_print(MSG_DEBUG, "CTRL_IFACE monitor attached", from, fromlen);
130 	return 0;
131 }
132 
133 
ctrl_iface_detach(struct dl_list * ctrl_dst,struct sockaddr_storage * from,socklen_t fromlen)134 int ctrl_iface_detach(struct dl_list *ctrl_dst, struct sockaddr_storage *from,
135 		      socklen_t fromlen)
136 {
137 	struct wpa_ctrl_dst *dst;
138 
139 	dl_list_for_each(dst, ctrl_dst, struct wpa_ctrl_dst, list) {
140 		if (!sockaddr_compare(from, fromlen,
141 				      &dst->addr, dst->addrlen)) {
142 			sockaddr_print(MSG_DEBUG, "CTRL_IFACE monitor detached",
143 				       from, fromlen);
144 			dl_list_del(&dst->list);
145 			os_free(dst);
146 			return 0;
147 		}
148 	}
149 
150 	return -1;
151 }
152 
153 
ctrl_iface_level(struct dl_list * ctrl_dst,struct sockaddr_storage * from,socklen_t fromlen,const char * level)154 int ctrl_iface_level(struct dl_list *ctrl_dst, struct sockaddr_storage *from,
155 		     socklen_t fromlen, const char *level)
156 {
157 	struct wpa_ctrl_dst *dst;
158 
159 	wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level);
160 
161 	dl_list_for_each(dst, ctrl_dst, struct wpa_ctrl_dst, list) {
162 		if (!sockaddr_compare(from, fromlen,
163 				      &dst->addr, dst->addrlen)) {
164 			sockaddr_print(MSG_DEBUG,
165 				       "CTRL_IFACE changed monitor level",
166 				       from, fromlen);
167 			dst->debug_level = atoi(level);
168 			return 0;
169 		}
170 	}
171 
172 	return -1;
173 }
174