505926f0ebef4deb9e66f4323caa82703dfe7b6a
[ppp.git] / pppd / sys-bsd.c
1 /*
2  * sys-bsd.c - System-dependent procedures for setting up
3  * PPP interfaces on bsd-4.4-ish systems (including 386BSD, NetBSD, etc.)
4  *
5  * Copyright (c) 1989 Carnegie Mellon University.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms are permitted
9  * provided that the above copyright notice and this paragraph are
10  * duplicated in all such forms and that any documentation,
11  * advertising materials, and other materials related to such
12  * distribution and use acknowledge that the software was developed
13  * by Carnegie Mellon University.  The name of the
14  * University may not be used to endorse or promote products derived
15  * from this software without specific prior written permission.
16  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
18  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19  */
20
21 #ifndef lint
22 static char rcsid[] = "$Id: sys-bsd.c,v 1.16 1995/04/24 05:37:51 paulus Exp $";
23 #endif
24
25 /*
26  * TODO:
27  */
28
29 #include <syslog.h>
30 #include <string.h>
31 #include <termios.h>
32 #include <sys/ioctl.h>
33 #include <sys/types.h>
34 #include <sys/socket.h>
35 #include <sys/time.h>
36 #include <sys/errno.h>
37
38 #include <net/if.h>
39 #include <net/ppp_defs.h>
40 #include <net/if_ppp.h>
41 #include <net/route.h>
42 #include <net/if_dl.h>
43 #include <netinet/in.h>
44
45 #if RTM_VERSION >= 3
46 #include <netinet/if_ether.h>
47 #endif
48
49 #include "pppd.h"
50
51 static int initdisc = -1;       /* Initial TTY discipline */
52 static int rtm_seq;
53
54 static int      restore_term;   /* 1 => we've munged the terminal */
55 static struct termios inittermios; /* Initial TTY termios */
56
57 int sockfd;                     /* socket for doing interface ioctls */
58
59 /*
60  * sys_init - System-dependent initialization.
61  */
62 void
63 sys_init()
64 {
65     openlog("pppd", LOG_PID | LOG_NDELAY, LOG_PPP);
66     setlogmask(LOG_UPTO(LOG_INFO));
67     if (debug)
68         setlogmask(LOG_UPTO(LOG_DEBUG));
69
70     /* Get an internet socket for doing socket ioctl's on. */
71     if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
72         syslog(LOG_ERR, "Couldn't create IP socket: %m");
73         die(1);
74     }
75 }
76
77 /*
78  * note_debug_level - note a change in the debug level.
79  */
80 void
81 note_debug_level()
82 {
83     if (debug) {
84         syslog(LOG_INFO, "Debug turned ON, Level %d", debug);
85         setlogmask(LOG_UPTO(LOG_DEBUG));
86     } else {
87         setlogmask(LOG_UPTO(LOG_WARNING));
88     }
89 }
90
91 /*
92  * ppp_available - check whether the system has any ppp interfaces
93  * (in fact we check whether we can do an ioctl on ppp0).
94  */
95 int
96 ppp_available()
97 {
98     int s, ok;
99     struct ifreq ifr;
100
101     if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
102         return 1;               /* can't tell - maybe we're not root */
103
104     strncpy(ifr.ifr_name, "ppp0", sizeof (ifr.ifr_name));
105     ok = ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) >= 0;
106     close(s);
107
108     return ok;
109 }
110
111 /*
112  * establish_ppp - Turn the serial port into a ppp interface.
113  */
114 void
115 establish_ppp()
116 {
117     int pppdisc = PPPDISC;
118     int x;
119
120     if (ioctl(fd, TIOCGETD, &initdisc) < 0) {
121         syslog(LOG_ERR, "ioctl(TIOCGETD): %m");
122         die(1);
123     }
124     if (ioctl(fd, TIOCSETD, &pppdisc) < 0) {
125         syslog(LOG_ERR, "ioctl(TIOCSETD): %m");
126         die(1);
127     }
128
129     /*
130      * Find out which interface we were given.
131      */
132     if (ioctl(fd, PPPIOCGUNIT, &ifunit) < 0) {  
133         syslog(LOG_ERR, "ioctl(PPPIOCGUNIT): %m");
134         die(1);
135     }
136
137     /*
138      * Enable debug in the driver if requested.
139      */
140     if (kdebugflag) {
141         if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) {
142             syslog(LOG_WARNING, "ioctl (PPPIOCGFLAGS): %m");
143         } else {
144             x |= (kdebugflag & 0xFF) * SC_DEBUG;
145             if (ioctl(fd, PPPIOCSFLAGS, (caddr_t) &x) < 0)
146                 syslog(LOG_WARNING, "ioctl(PPPIOCSFLAGS): %m");
147         }
148     }
149 }
150
151
152 /*
153  * disestablish_ppp - Restore the serial port to normal operation.
154  * This shouldn't call die() because it's called from die().
155  */
156 void
157 disestablish_ppp()
158 {
159     int x;
160     char *s;
161
162     if (initdisc >= 0) {
163         /*
164          * Check whether the link seems not to be 8-bit clean.
165          */
166         if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &x) == 0) {
167             s = NULL;
168             switch (~x & (SC_RCV_B7_0|SC_RCV_B7_1|SC_RCV_EVNP|SC_RCV_ODDP)) {
169             case SC_RCV_B7_0:
170                 s = "bit 7 set to 1";
171                 break;
172             case SC_RCV_B7_1:
173                 s = "bit 7 set to 0";
174                 break;
175             case SC_RCV_EVNP:
176                 s = "odd parity";
177                 break;
178             case SC_RCV_ODDP:
179                 s = "even parity";
180                 break;
181             }
182             if (s != NULL) {
183                 syslog(LOG_WARNING, "Serial link is not 8-bit clean:");
184                 syslog(LOG_WARNING, "All received characters had %s", s);
185             }
186         }
187         if (ioctl(fd, TIOCSETD, &initdisc) < 0)
188             syslog(LOG_ERR, "ioctl(TIOCSETD): %m");
189     }
190 }
191
192
193 /*
194  * set_up_tty: Set up the serial port on `fd' for 8 bits, no parity,
195  * at the requested speed, etc.  If `local' is true, set CLOCAL
196  * regardless of whether the modem option was specified.
197  *
198  * For *BSD, we assume that speed_t values numerically equal bits/second.
199  */
200 set_up_tty(fd, local)
201     int fd, local;
202 {
203     struct termios tios;
204
205     if (tcgetattr(fd, &tios) < 0) {
206         syslog(LOG_ERR, "tcgetattr: %m");
207         die(1);
208     }
209
210     if (!restore_term)
211         inittermios = tios;
212
213     tios.c_cflag &= ~(CSIZE | CSTOPB | PARENB | CLOCAL);
214     if (crtscts > 0)
215         tios.c_cflag |= CRTSCTS;
216     else if (crtscts < 0)
217         tios.c_cflag &= ~CRTSCTS;
218
219     tios.c_cflag |= CS8 | CREAD | HUPCL;
220     if (local || !modem)
221         tios.c_cflag |= CLOCAL;
222     tios.c_iflag = IGNBRK | IGNPAR;
223     tios.c_oflag = 0;
224     tios.c_lflag = 0;
225     tios.c_cc[VMIN] = 1;
226     tios.c_cc[VTIME] = 0;
227
228     if (crtscts == 2) {
229         tios.c_iflag |= IXOFF;
230         tios.c_cc[VSTOP] = 0x13;        /* DC3 = XOFF = ^S */
231         tios.c_cc[VSTART] = 0x11;       /* DC1 = XON  = ^Q */
232     }
233
234     if (inspeed) {
235         cfsetospeed(&tios, inspeed);
236         cfsetispeed(&tios, inspeed);
237     } else {
238         inspeed = cfgetospeed(&tios);
239         /*
240          * We can't proceed if the serial port speed is 0,
241          * since that implies that the serial port is disabled.
242          */
243         if (inspeed == 0) {
244             syslog(LOG_ERR, "Baud rate for %s is 0; need explicit baud rate",
245                    devnam);
246             die(1);
247         }
248     }
249     baud_rate = inspeed;
250
251     if (tcsetattr(fd, TCSAFLUSH, &tios) < 0) {
252         syslog(LOG_ERR, "tcsetattr: %m");
253         die(1);
254     }
255
256     restore_term = 1;
257 }
258
259 /*
260  * restore_tty - restore the terminal to the saved settings.
261  */
262 void
263 restore_tty()
264 {
265     if (restore_term) {
266         if (tcsetattr(fd, TCSAFLUSH, &inittermios) < 0)
267             if (errno != ENXIO)
268                 syslog(LOG_WARNING, "tcsetattr: %m");
269         restore_term = 0;
270     }
271 }
272
273 /*
274  * setdtr - control the DTR line on the serial port.
275  * This is called from die(), so it shouldn't call die().
276  */
277 setdtr(fd, on)
278 int fd, on;
279 {
280     int modembits = TIOCM_DTR;
281
282     ioctl(fd, (on? TIOCMBIS: TIOCMBIC), &modembits);
283 }
284
285
286 /*
287  * output - Output PPP packet.
288  */
289 void
290 output(unit, p, len)
291     int unit;
292     u_char *p;
293     int len;
294 {
295     if (unit != 0)
296         MAINDEBUG((LOG_WARNING, "output: unit != 0!"));
297     if (debug)
298         log_packet(p, len, "sent ");
299
300     if (write(fd, p, len) < 0) {
301         syslog(LOG_ERR, "write: %m");
302         die(1);
303     }
304 }
305
306
307 /*
308  * wait_input - wait until there is data available on fd,
309  * for the length of time specified by *timo (indefinite
310  * if timo is NULL).
311  */
312 wait_input(timo)
313     struct timeval *timo;
314 {
315     fd_set ready;
316     int n;
317
318     FD_ZERO(&ready);
319     FD_SET(fd, &ready);
320     n = select(fd+1, &ready, NULL, &ready, timo);
321     if (n < 0 && errno != EINTR) {
322         syslog(LOG_ERR, "select: %m");
323         die(1);
324     }
325 }
326
327
328 /*
329  * read_packet - get a PPP packet from the serial device.
330  */
331 int
332 read_packet(buf)
333     u_char *buf;
334 {
335     int len;
336
337     if ((len = read(fd, buf, PPP_MTU + PPP_HDRLEN)) < 0) {
338         if (errno == EWOULDBLOCK || errno == EINTR) {
339             MAINDEBUG((LOG_DEBUG, "read(fd): %m"));
340             return -1;
341         }
342         syslog(LOG_ERR, "read(fd): %m");
343         die(1);
344     }
345     return len;
346 }
347
348
349 /*
350  * ppp_send_config - configure the transmit characteristics of
351  * the ppp interface.
352  */
353 void
354 ppp_send_config(unit, mtu, asyncmap, pcomp, accomp)
355     int unit, mtu;
356     u_int32_t asyncmap;
357     int pcomp, accomp;
358 {
359     u_int x;
360     struct ifreq ifr;
361
362     strncpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
363     ifr.ifr_mtu = mtu;
364     if (ioctl(sockfd, SIOCSIFMTU, (caddr_t) &ifr) < 0) {
365         syslog(LOG_ERR, "ioctl(SIOCSIFMTU): %m");
366         quit();
367     }
368
369     if (ioctl(fd, PPPIOCSASYNCMAP, (caddr_t) &asyncmap) < 0) {
370         syslog(LOG_ERR, "ioctl(PPPIOCSASYNCMAP): %m");
371         quit();
372     }
373
374     if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) {
375         syslog(LOG_ERR, "ioctl (PPPIOCGFLAGS): %m");
376         quit();
377     }
378     x = pcomp? x | SC_COMP_PROT: x &~ SC_COMP_PROT;
379     x = accomp? x | SC_COMP_AC: x &~ SC_COMP_AC;
380     if (ioctl(fd, PPPIOCSFLAGS, (caddr_t) &x) < 0) {
381         syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m");
382         quit();
383     }
384 }
385
386
387 /*
388  * ppp_set_xaccm - set the extended transmit ACCM for the interface.
389  */
390 void
391 ppp_set_xaccm(unit, accm)
392     int unit;
393     ext_accm accm;
394 {
395     if (ioctl(fd, PPPIOCSXASYNCMAP, accm) < 0 && errno != ENOTTY)
396         syslog(LOG_WARNING, "ioctl(set extended ACCM): %m");
397 }
398
399
400 /*
401  * ppp_recv_config - configure the receive-side characteristics of
402  * the ppp interface.
403  */
404 void
405 ppp_recv_config(unit, mru, asyncmap, pcomp, accomp)
406     int unit, mru;
407     u_int32_t asyncmap;
408     int pcomp, accomp;
409 {
410     int x;
411
412     if (ioctl(fd, PPPIOCSMRU, (caddr_t) &mru) < 0) {
413         syslog(LOG_ERR, "ioctl(PPPIOCSMRU): %m");
414         quit();
415     }
416     if (ioctl(fd, PPPIOCSRASYNCMAP, (caddr_t) &asyncmap) < 0) {
417         syslog(LOG_ERR, "ioctl(PPPIOCSRASYNCMAP): %m");
418         quit();
419     }
420     if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) {
421         syslog(LOG_ERR, "ioctl (PPPIOCGFLAGS): %m");
422         quit();
423     }
424     x = !accomp? x | SC_REJ_COMP_AC: x &~ SC_REJ_COMP_AC;
425     if (ioctl(fd, PPPIOCSFLAGS, (caddr_t) &x) < 0) {
426         syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m");
427         quit();
428     }
429 }
430
431 /*
432  * ccp_test - ask kernel whether a given compression method
433  * is acceptable for use.
434  */
435 ccp_test(unit, opt_ptr, opt_len, for_transmit)
436     int unit, opt_len, for_transmit;
437     u_char *opt_ptr;
438 {
439     struct ppp_option_data data;
440
441     data.ptr = opt_ptr;
442     data.length = opt_len;
443     data.transmit = for_transmit;
444     return ioctl(fd, PPPIOCSCOMPRESS, (caddr_t) &data) >= 0;
445 }
446
447 /*
448  * ccp_flags_set - inform kernel about the current state of CCP.
449  */
450 void
451 ccp_flags_set(unit, isopen, isup)
452     int unit, isopen, isup;
453 {
454     int x;
455
456     if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) {
457         syslog(LOG_ERR, "ioctl (PPPIOCGFLAGS): %m");
458         return;
459     }
460     x = isopen? x | SC_CCP_OPEN: x &~ SC_CCP_OPEN;
461     x = isup? x | SC_CCP_UP: x &~ SC_CCP_UP;
462     if (ioctl(fd, PPPIOCSFLAGS, (caddr_t) &x) < 0)
463         syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m");
464 }
465
466 /*
467  * ccp_fatal_error - returns 1 if decompression was disabled as a
468  * result of an error detected after decompression of a packet,
469  * 0 otherwise.  This is necessary because of patent nonsense.
470  */
471 int
472 ccp_fatal_error(unit)
473     int unit;
474 {
475     int x;
476
477     if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) {
478         syslog(LOG_ERR, "ioctl(PPPIOCGFLAGS): %m");
479         return 0;
480     }
481     return x & SC_DC_FERROR;
482 }
483
484 /*
485  * sifvjcomp - config tcp header compression
486  */
487 int
488 sifvjcomp(u, vjcomp, cidcomp, maxcid)
489     int u, vjcomp, cidcomp, maxcid;
490 {
491     u_int x;
492
493     if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) {
494         syslog(LOG_ERR, "ioctl (PPPIOCGFLAGS): %m");
495         return 0;
496     }
497     x = vjcomp ? x | SC_COMP_TCP: x &~ SC_COMP_TCP;
498     x = cidcomp? x & ~SC_NO_TCP_CCID: x | SC_NO_TCP_CCID;
499     if (ioctl(fd, PPPIOCSFLAGS, (caddr_t) &x) < 0) {
500         syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m");
501         return 0;
502     }
503     if (ioctl(fd, PPPIOCSMAXCID, (caddr_t) &maxcid) < 0) {
504         syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m");
505         return 0;
506     }
507     return 1;
508 }
509
510 /*
511  * sifup - Config the interface up and enable IP packets to pass.
512  */
513 #ifndef SC_ENABLE_IP
514 #define SC_ENABLE_IP    0x100   /* compat for old versions of kernel code */
515 #endif
516
517 int
518 sifup(u)
519     int u;
520 {
521     struct ifreq ifr;
522     u_int x;
523     struct npioctl npi;
524
525     strncpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
526     if (ioctl(sockfd, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
527         syslog(LOG_ERR, "ioctl (SIOCGIFFLAGS): %m");
528         return 0;
529     }
530     ifr.ifr_flags |= IFF_UP;
531     if (ioctl(sockfd, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
532         syslog(LOG_ERR, "ioctl(SIOCSIFFLAGS): %m");
533         return 0;
534     }
535     npi.protocol = PPP_IP;
536     npi.mode = NPMODE_PASS;
537     if (ioctl(fd, PPPIOCSNPMODE, &npi) < 0) {
538         if (errno != ENOTTY) {
539             syslog(LOG_ERR, "ioctl(PPPIOCSNPMODE): %m");
540             return 0;
541         }
542         /* for backwards compatibility */
543         if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) {
544             syslog(LOG_ERR, "ioctl (PPPIOCGFLAGS): %m");
545             return 0;
546         }
547         x |= SC_ENABLE_IP;
548         if (ioctl(fd, PPPIOCSFLAGS, (caddr_t) &x) < 0) {
549             syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m");
550             return 0;
551         }
552     }
553     return 1;
554 }
555
556 /*
557  * sifdown - Config the interface down and disable IP.
558  */
559 int
560 sifdown(u)
561     int u;
562 {
563     struct ifreq ifr;
564     u_int x;
565     int rv;
566     struct npioctl npi;
567
568     rv = 1;
569     npi.protocol = PPP_IP;
570     npi.mode = NPMODE_ERROR;
571     if (ioctl(fd, PPPIOCSNPMODE, (caddr_t) &npi) < 0) {
572         if (errno != ENOTTY) {
573             syslog(LOG_ERR, "ioctl(PPPIOCSNPMODE): %m");
574             rv = 0;
575         } else {
576             /* backwards compatibility */
577             if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) {
578                 syslog(LOG_ERR, "ioctl (PPPIOCGFLAGS): %m");
579                 rv = 0;
580             } else {
581                 x &= ~SC_ENABLE_IP;
582                 if (ioctl(fd, PPPIOCSFLAGS, (caddr_t) &x) < 0) {
583                     syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m");
584                     rv = 0;
585                 }
586             }
587         }
588     }
589
590     strncpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
591     if (ioctl(sockfd, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
592         syslog(LOG_ERR, "ioctl (SIOCGIFFLAGS): %m");
593         rv = 0;
594     } else {
595         ifr.ifr_flags &= ~IFF_UP;
596         if (ioctl(sockfd, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
597             syslog(LOG_ERR, "ioctl(SIOCSIFFLAGS): %m");
598             rv = 0;
599         }
600     }
601     return rv;
602 }
603
604 /*
605  * SET_SA_FAMILY - set the sa_family field of a struct sockaddr,
606  * if it exists.
607  */
608 #define SET_SA_FAMILY(addr, family)             \
609     BZERO((char *) &(addr), sizeof(addr));      \
610     addr.sa_family = (family);                  \
611     addr.sa_len = sizeof(addr);
612
613 /*
614  * sifaddr - Config the interface IP addresses and netmask.
615  */
616 int
617 sifaddr(u, o, h, m)
618     int u;
619     u_int32_t o, h, m;
620 {
621     struct ifaliasreq ifra;
622
623     strncpy(ifra.ifra_name, ifname, sizeof(ifra.ifra_name));
624     SET_SA_FAMILY(ifra.ifra_addr, AF_INET);
625     ((struct sockaddr_in *) &ifra.ifra_addr)->sin_addr.s_addr = o;
626     SET_SA_FAMILY(ifra.ifra_broadaddr, AF_INET);
627     ((struct sockaddr_in *) &ifra.ifra_broadaddr)->sin_addr.s_addr = h;
628     if (m != 0) {
629         SET_SA_FAMILY(ifra.ifra_mask, AF_INET);
630         ((struct sockaddr_in *) &ifra.ifra_mask)->sin_addr.s_addr = m;
631     } else
632         BZERO(&ifra.ifra_mask, sizeof(ifra.ifra_mask));
633     if (ioctl(sockfd, SIOCAIFADDR, (caddr_t) &ifra) < 0) {
634         if (errno != EEXIST) {
635             syslog(LOG_ERR, "ioctl(SIOCAIFADDR): %m");
636             return 0;
637         }
638         syslog(LOG_WARNING, "ioctl(SIOCAIFADDR): Address already exists");
639     }
640     return 1;
641 }
642
643 /*
644  * cifaddr - Clear the interface IP addresses, and delete routes
645  * through the interface if possible.
646  */
647 int
648 cifaddr(u, o, h)
649     int u;
650     u_int32_t o, h;
651 {
652     struct ifaliasreq ifra;
653
654     strncpy(ifra.ifra_name, ifname, sizeof(ifra.ifra_name));
655     SET_SA_FAMILY(ifra.ifra_addr, AF_INET);
656     ((struct sockaddr_in *) &ifra.ifra_addr)->sin_addr.s_addr = o;
657     SET_SA_FAMILY(ifra.ifra_broadaddr, AF_INET);
658     ((struct sockaddr_in *) &ifra.ifra_broadaddr)->sin_addr.s_addr = h;
659     BZERO(&ifra.ifra_mask, sizeof(ifra.ifra_mask));
660     if (ioctl(sockfd, SIOCDIFADDR, (caddr_t) &ifra) < 0) {
661         syslog(LOG_WARNING, "ioctl(SIOCDIFADDR): %m");
662         return 0;
663     }
664     return 1;
665 }
666
667 /*
668  * sifdefaultroute - assign a default route through the address given.
669  */
670 int
671 sifdefaultroute(u, g)
672     int u;
673     u_int32_t g;
674 {
675     return dodefaultroute(g, 's');
676 }
677
678 /*
679  * cifdefaultroute - delete a default route through the address given.
680  */
681 int
682 cifdefaultroute(u, g)
683     int u;
684     u_int32_t g;
685 {
686     return dodefaultroute(g, 'c');
687 }
688
689 /*
690  * dodefaultroute - talk to a routing socket to add/delete a default route.
691  */
692 int
693 dodefaultroute(g, cmd)
694     u_int32_t g;
695     int cmd;
696 {
697     int routes;
698     struct {
699         struct rt_msghdr        hdr;
700         struct sockaddr_in      dst;
701         struct sockaddr_in      gway;
702         struct sockaddr_in      mask;
703     } rtmsg;
704
705     if ((routes = socket(PF_ROUTE, SOCK_RAW, AF_INET)) < 0) {
706         syslog(LOG_ERR, "%cifdefaultroute: opening routing socket: %m", cmd);
707         return 0;
708     }
709
710     memset(&rtmsg, 0, sizeof(rtmsg));
711     rtmsg.hdr.rtm_type = cmd == 's'? RTM_ADD: RTM_DELETE;
712     rtmsg.hdr.rtm_flags = RTF_UP | RTF_GATEWAY;
713     rtmsg.hdr.rtm_version = RTM_VERSION;
714     rtmsg.hdr.rtm_seq = ++rtm_seq;
715     rtmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
716     rtmsg.dst.sin_len = sizeof(rtmsg.dst);
717     rtmsg.dst.sin_family = AF_INET;
718     rtmsg.gway.sin_len = sizeof(rtmsg.gway);
719     rtmsg.gway.sin_family = AF_INET;
720     rtmsg.gway.sin_addr.s_addr = g;
721     rtmsg.mask.sin_len = sizeof(rtmsg.dst);
722     rtmsg.mask.sin_family = AF_INET;
723
724     rtmsg.hdr.rtm_msglen = sizeof(rtmsg);
725     if (write(routes, &rtmsg, sizeof(rtmsg)) < 0) {
726         syslog(LOG_ERR, "%s default route: %m", cmd=='s'? "add": "delete");
727         close(routes);
728         return 0;
729     }
730
731     close(routes);
732     return 1;
733 }
734
735 #if RTM_VERSION >= 3
736
737 /*
738  * sifproxyarp - Make a proxy ARP entry for the peer.
739  */
740 static struct {
741     struct rt_msghdr            hdr;
742     struct sockaddr_inarp       dst;
743     struct sockaddr_dl          hwa;
744     char                        extra[128];
745 } arpmsg;
746
747 static int arpmsg_valid;
748
749 int
750 sifproxyarp(unit, hisaddr)
751     int unit;
752     u_int32_t hisaddr;
753 {
754     int routes;
755     int l;
756
757     /*
758      * Get the hardware address of an interface on the same subnet
759      * as our local address.
760      */
761     memset(&arpmsg, 0, sizeof(arpmsg));
762     if (!get_ether_addr(hisaddr, &arpmsg.hwa)) {
763         syslog(LOG_ERR, "Cannot determine ethernet address for proxy ARP");
764         return 0;
765     }
766
767     if ((routes = socket(PF_ROUTE, SOCK_RAW, AF_INET)) < 0) {
768         syslog(LOG_ERR, "sifproxyarp: opening routing socket: %m");
769         return 0;
770     }
771
772     arpmsg.hdr.rtm_type = RTM_ADD;
773     arpmsg.hdr.rtm_flags = RTF_ANNOUNCE | RTF_HOST | RTF_STATIC;
774     arpmsg.hdr.rtm_version = RTM_VERSION;
775     arpmsg.hdr.rtm_seq = ++rtm_seq;
776     arpmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY;
777     arpmsg.hdr.rtm_inits = RTV_EXPIRE;
778     arpmsg.dst.sin_len = sizeof(struct sockaddr_inarp);
779     arpmsg.dst.sin_family = AF_INET;
780     arpmsg.dst.sin_addr.s_addr = hisaddr;
781     arpmsg.dst.sin_other = SIN_PROXY;
782
783     arpmsg.hdr.rtm_msglen = (char *) &arpmsg.hwa - (char *) &arpmsg
784         + arpmsg.hwa.sdl_len;
785     if (write(routes, &arpmsg, arpmsg.hdr.rtm_msglen) < 0) {
786         syslog(LOG_ERR, "add proxy arp entry: %m");
787         close(routes);
788         return 0;
789     }
790
791     close(routes);
792     arpmsg_valid = 1;
793     return 1;
794 }
795
796 /*
797  * cifproxyarp - Delete the proxy ARP entry for the peer.
798  */
799 int
800 cifproxyarp(unit, hisaddr)
801     int unit;
802     u_int32_t hisaddr;
803 {
804     int routes;
805
806     if (!arpmsg_valid)
807         return 0;
808     arpmsg_valid = 0;
809
810     arpmsg.hdr.rtm_type = RTM_DELETE;
811     arpmsg.hdr.rtm_seq = ++rtm_seq;
812
813     if ((routes = socket(PF_ROUTE, SOCK_RAW, AF_INET)) < 0) {
814         syslog(LOG_ERR, "sifproxyarp: opening routing socket: %m");
815         return 0;
816     }
817
818     if (write(routes, &arpmsg, arpmsg.hdr.rtm_msglen) < 0) {
819         syslog(LOG_ERR, "delete proxy arp entry: %m");
820         close(routes);
821         return 0;
822     }
823
824     close(routes);
825     return 1;
826 }
827
828 #else   /* RTM_VERSION */
829
830 /*
831  * sifproxyarp - Make a proxy ARP entry for the peer.
832  */
833 int
834 sifproxyarp(unit, hisaddr)
835     int unit;
836     u_int32_t hisaddr;
837 {
838     struct arpreq arpreq;
839     struct {
840         struct sockaddr_dl      sdl;
841         char                    space[128];
842     } dls;
843
844     BZERO(&arpreq, sizeof(arpreq));
845
846     /*
847      * Get the hardware address of an interface on the same subnet
848      * as our local address.
849      */
850     if (!get_ether_addr(hisaddr, &dls.sdl)) {
851         syslog(LOG_ERR, "Cannot determine ethernet address for proxy ARP");
852         return 0;
853     }
854
855     arpreq.arp_ha.sa_len = sizeof(struct sockaddr);
856     arpreq.arp_ha.sa_family = AF_UNSPEC;
857     BCOPY(LLADDR(&dls.sdl), arpreq.arp_ha.sa_data, dls.sdl.sdl_alen);
858     SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
859     ((struct sockaddr_in *) &arpreq.arp_pa)->sin_addr.s_addr = hisaddr;
860     arpreq.arp_flags = ATF_PERM | ATF_PUBL;
861     if (ioctl(sockfd, SIOCSARP, (caddr_t)&arpreq) < 0) {
862         syslog(LOG_ERR, "ioctl(SIOCSARP): %m");
863         return 0;
864     }
865
866     return 1;
867 }
868
869 /*
870  * cifproxyarp - Delete the proxy ARP entry for the peer.
871  */
872 int
873 cifproxyarp(unit, hisaddr)
874     int unit;
875     u_int32_t hisaddr;
876 {
877     struct arpreq arpreq;
878
879     BZERO(&arpreq, sizeof(arpreq));
880     SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
881     ((struct sockaddr_in *) &arpreq.arp_pa)->sin_addr.s_addr = hisaddr;
882     if (ioctl(sockfd, SIOCDARP, (caddr_t)&arpreq) < 0) {
883         syslog(LOG_WARNING, "ioctl(SIOCDARP): %m");
884         return 0;
885     }
886     return 1;
887 }
888 #endif  /* RTM_VERSION */
889
890
891 /*
892  * get_ether_addr - get the hardware address of an interface on the
893  * the same subnet as ipaddr.
894  */
895 #define MAX_IFS         32
896
897 int
898 get_ether_addr(ipaddr, hwaddr)
899     u_int32_t ipaddr;
900     struct sockaddr_dl *hwaddr;
901 {
902     struct ifreq *ifr, *ifend, *ifp;
903     u_int32_t ina, mask;
904     struct sockaddr_dl *dla;
905     struct ifreq ifreq;
906     struct ifconf ifc;
907     struct ifreq ifs[MAX_IFS];
908
909     ifc.ifc_len = sizeof(ifs);
910     ifc.ifc_req = ifs;
911     if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) {
912         syslog(LOG_ERR, "ioctl(SIOCGIFCONF): %m");
913         return 0;
914     }
915
916     /*
917      * Scan through looking for an interface with an Internet
918      * address on the same subnet as `ipaddr'.
919      */
920     ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
921     for (ifr = ifc.ifc_req; ifr < ifend; ifr = (struct ifreq *)
922                 ((char *)&ifr->ifr_addr + ifr->ifr_addr.sa_len)) {
923         if (ifr->ifr_addr.sa_family == AF_INET) {
924             ina = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr;
925             strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
926             /*
927              * Check that the interface is up, and not point-to-point
928              * or loopback.
929              */
930             if (ioctl(sockfd, SIOCGIFFLAGS, &ifreq) < 0)
931                 continue;
932             if ((ifreq.ifr_flags &
933                  (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|IFF_LOOPBACK|IFF_NOARP))
934                  != (IFF_UP|IFF_BROADCAST))
935                 continue;
936             /*
937              * Get its netmask and check that it's on the right subnet.
938              */
939             if (ioctl(sockfd, SIOCGIFNETMASK, &ifreq) < 0)
940                 continue;
941             mask = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr;
942             if ((ipaddr & mask) != (ina & mask))
943                 continue;
944
945             break;
946         }
947     }
948
949     if (ifr >= ifend)
950         return 0;
951     syslog(LOG_INFO, "found interface %s for proxy arp", ifr->ifr_name);
952
953     /*
954      * Now scan through again looking for a link-level address
955      * for this interface.
956      */
957     ifp = ifr;
958     for (ifr = ifc.ifc_req; ifr < ifend; ) {
959         if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0
960             && ifr->ifr_addr.sa_family == AF_LINK) {
961             /*
962              * Found the link-level address - copy it out
963              */
964             dla = (struct sockaddr_dl *) &ifr->ifr_addr;
965             BCOPY(dla, hwaddr, dla->sdl_len);
966             return 1;
967         }
968         ifr = (struct ifreq *) ((char *)&ifr->ifr_addr + ifr->ifr_addr.sa_len);
969     }
970
971     return 0;
972 }