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