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_set_events(struct wpa_ctrl_dst * dst,const char * input)116 static int ctrl_set_events(struct wpa_ctrl_dst *dst, const char *input)
117 {
118           const char *value;
119           int val;
120 
121           if (!input)
122                     return 0;
123 
124           value = os_strchr(input, '=');
125           if (!value)
126                     return -1;
127           value++;
128           val = atoi(value);
129           if (val < 0 || val > 1)
130                     return -1;
131 
132           if (str_starts(input, "probe_rx_events=")) {
133                     if (val)
134                               dst->events |= WPA_EVENT_RX_PROBE_REQUEST;
135                     else
136                               dst->events &= ~WPA_EVENT_RX_PROBE_REQUEST;
137           }
138 
139           return 0;
140 }
141 
142 
ctrl_iface_attach(struct dl_list * ctrl_dst,struct sockaddr_storage * from,socklen_t fromlen,const char * input)143 int ctrl_iface_attach(struct dl_list *ctrl_dst, struct sockaddr_storage *from,
144                           socklen_t fromlen, const char *input)
145 {
146           struct wpa_ctrl_dst *dst;
147 
148           /* Update event registration if already attached */
149           dl_list_for_each(dst, ctrl_dst, struct wpa_ctrl_dst, list) {
150                     if (!sockaddr_compare(from, fromlen,
151                                               &dst->addr, dst->addrlen))
152                               return ctrl_set_events(dst, input);
153           }
154 
155           /* New attachment */
156           dst = os_zalloc(sizeof(*dst));
157           if (dst == NULL)
158                     return -1;
159           os_memcpy(&dst->addr, from, fromlen);
160           dst->addrlen = fromlen;
161           dst->debug_level = MSG_INFO;
162           ctrl_set_events(dst, input);
163           dl_list_add(ctrl_dst, &dst->list);
164 
165           sockaddr_print(MSG_DEBUG, "CTRL_IFACE monitor attached", from, fromlen);
166           return 0;
167 }
168 
169 
ctrl_iface_detach(struct dl_list * ctrl_dst,struct sockaddr_storage * from,socklen_t fromlen)170 int ctrl_iface_detach(struct dl_list *ctrl_dst, struct sockaddr_storage *from,
171                           socklen_t fromlen)
172 {
173           struct wpa_ctrl_dst *dst;
174 
175           dl_list_for_each(dst, ctrl_dst, struct wpa_ctrl_dst, list) {
176                     if (!sockaddr_compare(from, fromlen,
177                                               &dst->addr, dst->addrlen)) {
178                               sockaddr_print(MSG_DEBUG, "CTRL_IFACE monitor detached",
179                                                from, fromlen);
180                               dl_list_del(&dst->list);
181                               os_free(dst);
182                               return 0;
183                     }
184           }
185 
186           return -1;
187 }
188 
189 
ctrl_iface_level(struct dl_list * ctrl_dst,struct sockaddr_storage * from,socklen_t fromlen,const char * level)190 int ctrl_iface_level(struct dl_list *ctrl_dst, struct sockaddr_storage *from,
191                          socklen_t fromlen, const char *level)
192 {
193           struct wpa_ctrl_dst *dst;
194 
195           wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level);
196 
197           dl_list_for_each(dst, ctrl_dst, struct wpa_ctrl_dst, list) {
198                     if (!sockaddr_compare(from, fromlen,
199                                               &dst->addr, dst->addrlen)) {
200                               sockaddr_print(MSG_DEBUG,
201                                                "CTRL_IFACE changed monitor level",
202                                                from, fromlen);
203                               dst->debug_level = atoi(level);
204                               return 0;
205                     }
206           }
207 
208           return -1;
209 }
210