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
17static 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
77void 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
116static 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
143int 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
170int 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
190int 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