1 /*
2 * Copyright (c) 2006 Michele Marchetto <mydecay@openbeer.it>
3 * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
4 * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/types.h>
20 #include <sys/ioctl.h>
21 #include <sys/time.h>
22 #include <sys/socket.h>
23 #include <netinet/in.h>
24 #include <arpa/inet.h>
25 #include <net/if.h>
26 #include <net/if_types.h>
27 #include <ctype.h>
28 #include <err.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <string.h>
33 #include <event.h>
34
35 #include "mdnsd.h"
36 #include "log.h"
37
38 extern struct mdnsd_conf *conf;
39
40 int if_act_start(struct iface *);
41 int if_act_reset(struct iface *);
42
43 struct {
44 int state;
45 enum iface_event event;
46 enum iface_action action;
47 int new_state;
48 } iface_fsm[] = {
49 /* current state event that happened action to take resulting state */
50 {IF_STA_DOWN, IF_EVT_UP, IF_ACT_STRT, 0},
51 {IF_STA_ANY, IF_EVT_DOWN, IF_ACT_RST, IF_STA_DOWN},
52 {-1, IF_EVT_NOTHING, IF_ACT_NOTHING, 0},
53 };
54
55 const char * const if_action_names[] = {
56 "NOTHING",
57 "START",
58 "RESET"
59 };
60
61 const char * const if_event_names[] = {
62 "NOTHING",
63 "UP",
64 "DOWN",
65 };
66
67 int
if_fsm(struct iface * iface,enum iface_event event)68 if_fsm(struct iface *iface, enum iface_event event)
69 {
70 int old_state;
71 int new_state = 0;
72 int i, ret = 0;
73
74 old_state = iface->state;
75
76 for (i = 0; iface_fsm[i].state != -1; i++)
77 if ((iface_fsm[i].state & old_state) &&
78 (iface_fsm[i].event == event)) {
79 new_state = iface_fsm[i].new_state;
80 break;
81 }
82
83 if (iface_fsm[i].state == -1) {
84 /* event outside of the defined fsm, ignore it. */
85 log_debug("if_fsm: interface %s, "
86 "event '%s' not expected in state '%s'", iface->name,
87 if_event_name(event), if_state_name(old_state));
88 return (0);
89 }
90
91 switch (iface_fsm[i].action) {
92 case IF_ACT_STRT:
93 ret = if_act_start(iface);
94 break;
95 case IF_ACT_RST:
96 ret = if_act_reset(iface);
97 break;
98 case IF_ACT_NOTHING:
99 /* do nothing */
100 break;
101 }
102
103 if (ret) {
104 log_debug("if_fsm: error changing state for interface %s, "
105 "event '%s', state '%s'", iface->name, if_event_name(event),
106 if_state_name(old_state));
107 return (0);
108 }
109
110 if (new_state != 0)
111 iface->state = new_state;
112
113 log_debug("if_fsm: event '%s' resulted in action '%s' and changing "
114 "state for interface %s from '%s' to '%s'",
115 if_event_name(event), if_action_name(iface_fsm[i].action),
116 iface->name, if_state_name(old_state), if_state_name(iface->state));
117
118 return (ret);
119 }
120
121 struct iface *
if_find_index(u_short ifindex)122 if_find_index(u_short ifindex)
123 {
124 struct iface *iface;
125
126 LIST_FOREACH(iface, &conf->iface_list, entry) {
127 if (iface->ifindex == ifindex)
128 return (iface);
129 }
130
131 return (NULL);
132 }
133
134 struct iface *
if_find_iface(unsigned int ifindex,struct in_addr src)135 if_find_iface(unsigned int ifindex, struct in_addr src)
136 {
137 struct iface *iface = NULL;
138
139 /* returned interface needs to be active */
140 LIST_FOREACH(iface, &conf->iface_list, entry) {
141 if (ifindex != 0 && ifindex == iface->ifindex &&
142 (iface->addr.s_addr & iface->mask.s_addr) ==
143 (src.s_addr & iface->mask.s_addr))
144 /*
145 * XXX may fail on P2P links because src and dst don't
146 * have to share a common subnet on the otherhand
147 * checking something like this will help to support
148 * multiple networks configured on one interface.
149 */
150 return (iface);
151 }
152
153 return (NULL);
154 }
155
156 /* actions */
157 int
if_act_start(struct iface * iface)158 if_act_start(struct iface *iface)
159 {
160 struct in_addr addr;
161 struct timeval now;
162
163 if (!((iface->flags & IFF_UP) &&
164 (LINK_STATE_IS_UP(iface->linkstate) ||
165 (iface->linkstate == LINK_STATE_UNKNOWN &&
166 iface->media_type != IFT_CARP)))) {
167 log_debug("if_act_start: interface %s link down",
168 iface->name);
169 return (0);
170 }
171
172 gettimeofday(&now, NULL);
173 iface->uptime = now.tv_sec;
174
175 switch (iface->type) {
176 case IF_TYPE_POINTOPOINT:
177 case IF_TYPE_BROADCAST:
178 inet_aton(ALL_MDNS_DEVICES, &addr);
179 if (if_join_group(iface, &addr)) {
180 log_warn("if_act_start: error joining group %s, "
181 "interface %s", inet_ntoa(addr), iface->name);
182 return (-1);
183 }
184
185 iface->state = IF_STA_ACTIVE;
186 break;
187 default:
188 fatalx("if_act_start: unknown interface type");
189 }
190
191 /* publish all groups on this interface */
192 pg_publish_byiface(iface);
193
194 return (0);
195 }
196
197 int
if_act_reset(struct iface * iface)198 if_act_reset(struct iface *iface)
199 {
200 struct in_addr addr;
201
202 switch (iface->type) {
203 case IF_TYPE_POINTOPOINT:
204 case IF_TYPE_BROADCAST:
205 inet_aton(ALL_MDNS_DEVICES, &addr);
206 if (if_leave_group(iface, &addr)) {
207 log_warn("if_act_reset: error leaving group %s, "
208 "interface %s", inet_ntoa(addr), iface->name);
209 }
210 break;
211 default:
212 fatalx("if_act_reset: unknown interface type");
213 }
214
215 return (0);
216 }
217
218 const char *
if_event_name(int event)219 if_event_name(int event)
220 {
221 return (if_event_names[event]);
222 }
223
224 const char *
if_action_name(int action)225 if_action_name(int action)
226 {
227 return (if_action_names[action]);
228 }
229
230 /* misc */
231 int
if_set_mcast_ttl(int fd,u_int8_t ttl)232 if_set_mcast_ttl(int fd, u_int8_t ttl)
233 {
234 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
235 (char *)&ttl, sizeof(ttl)) < 0) {
236 log_warn("if_set_mcast_ttl: error setting "
237 "IP_MULTICAST_TTL to %d", ttl);
238 return (-1);
239 }
240
241 return (0);
242 }
243
244 int
if_set_opt(int fd)245 if_set_opt(int fd)
246 {
247 int yes = 1;
248
249 if (setsockopt(fd, IPPROTO_IP, IP_RECVIF, &yes,
250 sizeof(int)) < 0) {
251 log_warn("if_set_opt: error setting IP_RECVIF");
252 return (-1);
253 }
254
255 if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &yes,
256 sizeof(int)) < 0) {
257 log_warn("if_set_opt: error setting IP_RECVDSTADDR");
258 return (-1);
259 }
260
261 return (0);
262 }
263
264 int
if_set_tos(int fd,int tos)265 if_set_tos(int fd, int tos)
266 {
267 if (setsockopt(fd, IPPROTO_IP, IP_TOS,
268 (int *)&tos, sizeof(tos)) < 0) {
269 log_warn("if_set_tos: error setting IP_TOS to 0x%x", tos);
270 return (-1);
271 }
272
273 return (0);
274 }
275
276 int
if_set_mcast(struct iface * iface)277 if_set_mcast(struct iface *iface)
278 {
279 switch (iface->type) {
280 case IF_TYPE_POINTOPOINT:
281 case IF_TYPE_BROADCAST:
282 if (setsockopt(iface->fd, IPPROTO_IP, IP_MULTICAST_IF,
283 &iface->addr.s_addr, sizeof(iface->addr.s_addr)) < 0) {
284 log_debug("if_set_mcast: error setting "
285 "IP_MULTICAST_IF, interface %s", iface->name);
286 return (-1);
287 }
288 break;
289 default:
290 fatalx("if_set_mcast: unknown interface type");
291 }
292
293 return (0);
294 }
295
296 int
if_set_mcast_loop(int fd)297 if_set_mcast_loop(int fd)
298 {
299 u_int8_t loop = 0;
300
301 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
302 (char *)&loop, sizeof(loop)) < 0) {
303 log_warn("if_set_mcast_loop: error setting IP_MULTICAST_LOOP");
304 return (-1);
305 }
306
307 return (0);
308 }
309
310 void
if_set_recvbuf(int fd)311 if_set_recvbuf(int fd)
312 {
313 int bsize;
314
315 bsize = 65535;
316 while (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bsize,
317 sizeof(bsize)) == -1)
318 bsize /= 2;
319 }
320
321 int
if_join_group(struct iface * iface,struct in_addr * addr)322 if_join_group(struct iface *iface, struct in_addr *addr)
323 {
324 struct ip_mreq mreq;
325
326 switch (iface->type) {
327 case IF_TYPE_POINTOPOINT:
328 case IF_TYPE_BROADCAST:
329 mreq.imr_multiaddr.s_addr = addr->s_addr;
330 mreq.imr_interface.s_addr = iface->addr.s_addr;
331
332 if (setsockopt(iface->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
333 (void *)&mreq, sizeof(mreq)) < 0)
334 return (-1);
335 break;
336 default:
337 fatalx("if_join_group: unknown interface type");
338 }
339
340 return (0);
341 }
342
343 int
if_leave_group(struct iface * iface,struct in_addr * addr)344 if_leave_group(struct iface *iface, struct in_addr *addr)
345 {
346 struct ip_mreq mreq;
347
348 switch (iface->type) {
349 case IF_TYPE_POINTOPOINT:
350 case IF_TYPE_BROADCAST:
351 mreq.imr_multiaddr.s_addr = addr->s_addr;
352 mreq.imr_interface.s_addr = iface->addr.s_addr;
353
354 if (setsockopt(iface->fd, IPPROTO_IP, IP_DROP_MEMBERSHIP,
355 (void *)&mreq, sizeof(mreq)) < 0)
356 return (-1);
357 break;
358 default:
359 fatalx("if_leave_group: unknown interface type");
360 }
361
362 return (0);
363 }
364
365 struct iface *
if_new(struct kif * kif)366 if_new(struct kif *kif)
367 {
368 struct sockaddr_in *sain;
369 struct iface *iface;
370 struct ifreq *ifr;
371 int s;
372 int succeed = 0;
373
374 if ((iface = calloc(1, sizeof(*iface))) == NULL)
375 err(1, "if_new: calloc");
376
377 iface->state = IF_STA_DOWN;
378
379 strlcpy(iface->name, kif->ifname, sizeof(iface->name));
380
381 if ((ifr = calloc(1, sizeof(*ifr))) == NULL)
382 err(1, "if_new: calloc");
383
384 /* set up ifreq */
385 strlcpy(ifr->ifr_name, kif->ifname, sizeof(ifr->ifr_name));
386 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
387 err(1, "if_new: socket");
388
389 /* get type */
390 if (kif->flags & IFF_POINTOPOINT)
391 iface->type = IF_TYPE_POINTOPOINT;
392 if (kif->flags & IFF_BROADCAST &&
393 kif->flags & IFF_MULTICAST)
394 iface->type = IF_TYPE_BROADCAST;
395 if (kif->flags & IFF_LOOPBACK) {
396 iface->type = IF_TYPE_POINTOPOINT;
397 /* XXX protect loopback from sending packets over lo? */
398 }
399
400 /* get mtu, index and flags */
401 iface->mtu = kif->mtu;
402 iface->ifindex = kif->ifindex;
403 iface->flags = kif->flags;
404 iface->linkstate = kif->link_state;
405 iface->media_type = kif->media_type;
406 iface->baudrate = kif->baudrate;
407 iface->ea = kif->ea;
408
409 /* get address */
410 if (ioctl(s, SIOCGIFADDR, ifr) < 0) {
411 log_warn("if_new: cannot get address");
412 goto end;
413 }
414 sain = (struct sockaddr_in *)&ifr->ifr_addr;
415 iface->addr = sain->sin_addr;
416
417 /* get mask */
418 if (ioctl(s, SIOCGIFNETMASK, ifr) < 0) {
419 log_warn("if_new: cannot get mask");
420 goto end;
421 }
422 sain = (struct sockaddr_in *)&ifr->ifr_addr;
423 iface->mask = sain->sin_addr;
424
425 /* get p2p dst address */
426 if (kif->flags & IFF_POINTOPOINT) {
427 if (ioctl(s, SIOCGIFDSTADDR, ifr) < 0) {
428 log_warn("if_new: cannot get dst addr");
429 goto end;
430 }
431 sain = (struct sockaddr_in *)&ifr->ifr_addr;
432 iface->dst = sain->sin_addr;
433 }
434
435 /* get the primary group for this interface */
436 if (conf->no_workstation == 0)
437 iface->pge_workstation = pge_new_workstation(iface);
438
439 succeed = 1;
440
441 end:
442 if (!succeed) {
443 free(iface);
444 iface = NULL;
445 }
446
447 free(ifr);
448 close(s);
449
450 return (iface);
451 }
452