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