]> git.ozlabs.org Git - ppp.git/blob - pppd/sys-bsd.c
*** empty log message ***
[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.6 1994/05/24 11:27:56 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 #include "pppd.h"
43 #include "ppp.h"
44
45 static int initdisc = -1;               /* Initial TTY discipline */
46 extern int kdebugflag;
47
48 /*
49  * establish_ppp - Turn the serial port into a ppp interface.
50  */
51 void
52 establish_ppp()
53 {
54     int pppdisc = PPPDISC;
55     int x;
56
57     if (ioctl(fd, TIOCGETD, &initdisc) < 0) {
58         syslog(LOG_ERR, "ioctl(TIOCGETD): %m");
59         die(1);
60     }
61     if (ioctl(fd, TIOCSETD, &pppdisc) < 0) {
62         syslog(LOG_ERR, "ioctl(TIOCSETD): %m");
63         die(1);
64     }
65
66     /*
67      * Find out which interface we were given.
68      */
69     if (ioctl(fd, PPPIOCGUNIT, &ifunit) < 0) {  
70         syslog(LOG_ERR, "ioctl(PPPIOCGUNIT): %m");
71         die(1);
72     }
73
74     /*
75      * Enable debug in the driver if requested.
76      */
77     if (kdebugflag) {
78         if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) {
79             syslog(LOG_WARNING, "ioctl (PPPIOCGFLAGS): %m");
80         } else {
81             x |= (kdebugflag & 0xFF) * SC_DEBUG;
82             if (ioctl(fd, PPPIOCSFLAGS, (caddr_t) &x) < 0)
83                 syslog(LOG_WARNING, "ioctl(PPPIOCSFLAGS): %m");
84         }
85     }
86 }
87
88
89 /*
90  * disestablish_ppp - Restore the serial port to normal operation.
91  * This shouldn't call die() because it's called from die().
92  */
93 void
94 disestablish_ppp()
95 {
96     int x;
97     char *s;
98
99     if (initdisc >= 0) {
100         /*
101          * Check whether the link seems not to be 8-bit clean.
102          */
103         if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &x) == 0) {
104             s = NULL;
105             switch (~x & (SC_RCV_B7_0|SC_RCV_B7_1|SC_RCV_EVNP|SC_RCV_ODDP)) {
106             case SC_RCV_B7_0:
107                 s = "bit 7 set to 1";
108                 break;
109             case SC_RCV_B7_1:
110                 s = "bit 7 set to 0";
111                 break;
112             case SC_RCV_EVNP:
113                 s = "odd parity";
114                 break;
115             case SC_RCV_ODDP:
116                 s = "even parity";
117                 break;
118             }
119             if (s != NULL) {
120                 syslog(LOG_WARNING, "Serial link is not 8-bit clean:");
121                 syslog(LOG_WARNING, "All received characters had %s", s);
122             }
123         }
124         if (ioctl(fd, TIOCSETD, &initdisc) < 0)
125             syslog(LOG_ERR, "ioctl(TIOCSETD): %m");
126     }
127 }
128
129
130 /*
131  * output - Output PPP packet.
132  */
133 void
134 output(unit, p, len)
135     int unit;
136     u_char *p;
137     int len;
138 {
139     if (unit != 0)
140         MAINDEBUG((LOG_WARNING, "output: unit != 0!"));
141     if (debug)
142         log_packet(p, len, "sent ");
143
144     if (write(fd, p, len) < 0) {
145         syslog(LOG_ERR, "write: %m");
146         die(1);
147     }
148 }
149
150
151 /*
152  * read_packet - get a PPP packet from the serial device.
153  */
154 int
155 read_packet(buf)
156     u_char *buf;
157 {
158     int len;
159
160     if ((len = read(fd, buf, MTU + DLLHEADERLEN)) < 0) {
161         if (errno == EWOULDBLOCK) {
162             MAINDEBUG((LOG_DEBUG, "read(fd): EWOULDBLOCK"));
163             return -1;
164         }
165         syslog(LOG_ERR, "read(fd): %m");
166         die(1);
167     }
168     return len;
169 }
170
171
172 /*
173  * ppp_send_config - configure the transmit characteristics of
174  * the ppp interface.
175  */
176 void
177 ppp_send_config(unit, mtu, asyncmap, pcomp, accomp)
178     int unit, mtu;
179     u_long asyncmap;
180     int pcomp, accomp;
181 {
182     u_int x;
183     struct ifreq ifr;
184
185     strncpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
186     ifr.ifr_mtu = mtu;
187     if (ioctl(s, SIOCSIFMTU, (caddr_t) &ifr) < 0) {
188         syslog(LOG_ERR, "ioctl(SIOCSIFMTU): %m");
189         quit();
190     }
191
192     if (ioctl(fd, PPPIOCSASYNCMAP, (caddr_t) &asyncmap) < 0) {
193         syslog(LOG_ERR, "ioctl(PPPIOCSASYNCMAP): %m");
194         quit();
195     }
196
197     if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) {
198         syslog(LOG_ERR, "ioctl (PPPIOCGFLAGS): %m");
199         quit();
200     }
201     x = pcomp? x | SC_COMP_PROT: x &~ SC_COMP_PROT;
202     x = accomp? x | SC_COMP_AC: x &~ SC_COMP_AC;
203     if (ioctl(fd, PPPIOCSFLAGS, (caddr_t) &x) < 0) {
204         syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m");
205         quit();
206     }
207 }
208
209
210 /*
211  * ppp_set_xaccm - set the extended transmit ACCM for the interface.
212  */
213 void
214 ppp_set_xaccm(unit, accm)
215     int unit;
216     ext_accm accm;
217 {
218     if (ioctl(fd, PPPIOCSXASYNCMAP, accm) < 0 && errno != ENOTTY)
219         syslog(LOG_WARNING, "ioctl(set extended ACCM): %m");
220 }
221
222
223 /*
224  * ppp_recv_config - configure the receive-side characteristics of
225  * the ppp interface.
226  */
227 void
228 ppp_recv_config(unit, mru, asyncmap, pcomp, accomp)
229     int unit, mru;
230     u_long asyncmap;
231     int pcomp, accomp;
232 {
233     int x;
234
235     if (ioctl(fd, PPPIOCSMRU, (caddr_t) &mru) < 0) {
236         syslog(LOG_ERR, "ioctl(PPPIOCSMRU): %m");
237         quit();
238     }
239     if (ioctl(fd, PPPIOCSRASYNCMAP, (caddr_t) &asyncmap) < 0) {
240         syslog(LOG_ERR, "ioctl(PPPIOCSRASYNCMAP): %m");
241         quit();
242     }
243     if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) {
244         syslog(LOG_ERR, "ioctl (PPPIOCGFLAGS): %m");
245         quit();
246     }
247     x = !accomp? x | SC_REJ_COMP_AC: x &~ SC_REJ_COMP_AC;
248     if (ioctl(fd, PPPIOCSFLAGS, (caddr_t) &x) < 0) {
249         syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m");
250         quit();
251     }
252 }
253
254 /*
255  * sifvjcomp - config tcp header compression
256  */
257 int
258 sifvjcomp(u, vjcomp, cidcomp, maxcid)
259     int u, vjcomp, cidcomp, maxcid;
260 {
261     u_int x;
262
263     if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) {
264         syslog(LOG_ERR, "ioctl (PPPIOCGFLAGS): %m");
265         return 0;
266     }
267     x = vjcomp ? x | SC_COMP_TCP: x &~ SC_COMP_TCP;
268     x = cidcomp? x & ~SC_NO_TCP_CCID: x | SC_NO_TCP_CCID;
269     if (ioctl(fd, PPPIOCSFLAGS, (caddr_t) &x) < 0) {
270         syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m");
271         return 0;
272     }
273     if (ioctl(fd, PPPIOCSMAXCID, (caddr_t) &maxcid) < 0) {
274         syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m");
275         return 0;
276     }
277     return 1;
278 }
279
280 /*
281  * sifup - Config the interface up and enable IP packets to pass.
282  */
283 int
284 sifup(u)
285     int u;
286 {
287     struct ifreq ifr;
288     u_int x;
289
290     strncpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
291     if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
292         syslog(LOG_ERR, "ioctl (SIOCGIFFLAGS): %m");
293         return 0;
294     }
295     ifr.ifr_flags |= IFF_UP;
296     if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
297         syslog(LOG_ERR, "ioctl(SIOCSIFFLAGS): %m");
298         return 0;
299     }
300     if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) {
301         syslog(LOG_ERR, "ioctl (PPPIOCGFLAGS): %m");
302         return 0;
303     }
304     x |= SC_ENABLE_IP;
305     if (ioctl(fd, PPPIOCSFLAGS, (caddr_t) &x) < 0) {
306         syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m");
307         return 0;
308     }
309     return 1;
310 }
311
312 /*
313  * sifdown - Config the interface down and disable IP.
314  */
315 int
316 sifdown(u)
317     int u;
318 {
319     struct ifreq ifr;
320     u_int x;
321     int rv;
322
323     rv = 1;
324     if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) {
325         syslog(LOG_ERR, "ioctl (PPPIOCGFLAGS): %m");
326         rv = 0;
327     } else {
328         x &= ~SC_ENABLE_IP;
329         if (ioctl(fd, PPPIOCSFLAGS, (caddr_t) &x) < 0) {
330             syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m");
331             rv = 0;
332         }
333     }
334     strncpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
335     if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
336         syslog(LOG_ERR, "ioctl (SIOCGIFFLAGS): %m");
337         rv = 0;
338     } else {
339         ifr.ifr_flags &= ~IFF_UP;
340         if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
341             syslog(LOG_ERR, "ioctl(SIOCSIFFLAGS): %m");
342             rv = 0;
343         }
344     }
345     return rv;
346 }
347
348 /*
349  * SET_SA_FAMILY - set the sa_family field of a struct sockaddr,
350  * if it exists.
351  */
352 #define SET_SA_FAMILY(addr, family)             \
353     BZERO((char *) &(addr), sizeof(addr));      \
354     addr.sa_family = (family);                  \
355     addr.sa_len = sizeof(addr);
356
357 /*
358  * sifaddr - Config the interface IP addresses and netmask.
359  */
360 int
361 sifaddr(u, o, h, m)
362     int u;
363     u_long o, h, m;
364 {
365     struct ifaliasreq ifra;
366
367     strncpy(ifra.ifra_name, ifname, sizeof(ifra.ifra_name));
368     SET_SA_FAMILY(ifra.ifra_addr, AF_INET);
369     ((struct sockaddr_in *) &ifra.ifra_addr)->sin_addr.s_addr = o;
370     SET_SA_FAMILY(ifra.ifra_broadaddr, AF_INET);
371     ((struct sockaddr_in *) &ifra.ifra_broadaddr)->sin_addr.s_addr = h;
372     if (m != 0) {
373         SET_SA_FAMILY(ifra.ifra_mask, AF_INET);
374         ((struct sockaddr_in *) &ifra.ifra_mask)->sin_addr.s_addr = m;
375     } else
376         BZERO(&ifra.ifra_mask, sizeof(ifra.ifra_mask));
377     if (ioctl(s, SIOCAIFADDR, (caddr_t) &ifra) < 0) {
378         if (errno != EEXIST) {
379             syslog(LOG_ERR, "ioctl(SIOCAIFADDR): %m");
380             return 0;
381         }
382         syslog(LOG_WARNING, "ioctl(SIOCAIFADDR): Address already exists");
383     }
384     return 1;
385 }
386
387 /*
388  * cifaddr - Clear the interface IP addresses, and delete routes
389  * through the interface if possible.
390  */
391 int
392 cifaddr(u, o, h)
393     int u;
394     u_long o, h;
395 {
396     struct ifaliasreq ifra;
397
398     strncpy(ifra.ifra_name, ifname, sizeof(ifra.ifra_name));
399     SET_SA_FAMILY(ifra.ifra_addr, AF_INET);
400     ((struct sockaddr_in *) &ifra.ifra_addr)->sin_addr.s_addr = o;
401     SET_SA_FAMILY(ifra.ifra_broadaddr, AF_INET);
402     ((struct sockaddr_in *) &ifra.ifra_broadaddr)->sin_addr.s_addr = h;
403     BZERO(&ifra.ifra_mask, sizeof(ifra.ifra_mask));
404     if (ioctl(s, SIOCDIFADDR, (caddr_t) &ifra) < 0) {
405         syslog(LOG_WARNING, "ioctl(SIOCDIFADDR): %m");
406         return 0;
407     }
408     return 1;
409 }
410
411 /*
412  * sifdefaultroute - assign a default route through the address given.
413  */
414 int
415 sifdefaultroute(u, g)
416     int u;
417     u_long g;
418 {
419     struct ortentry rt;
420
421     SET_SA_FAMILY(rt.rt_dst, AF_INET);
422     SET_SA_FAMILY(rt.rt_gateway, AF_INET);
423     ((struct sockaddr_in *) &rt.rt_gateway)->sin_addr.s_addr = g;
424     rt.rt_flags = RTF_GATEWAY;
425     if (ioctl(s, SIOCADDRT, &rt) < 0) {
426         syslog(LOG_ERR, "default route ioctl(SIOCADDRT): %m");
427         return 0;
428     }
429     return 1;
430 }
431
432 /*
433  * cifdefaultroute - delete a default route through the address given.
434  */
435 int
436 cifdefaultroute(u, g)
437     int u;
438     u_long g;
439 {
440     struct ortentry rt;
441
442     SET_SA_FAMILY(rt.rt_dst, AF_INET);
443     SET_SA_FAMILY(rt.rt_gateway, AF_INET);
444     ((struct sockaddr_in *) &rt.rt_gateway)->sin_addr.s_addr = g;
445     rt.rt_flags = RTF_GATEWAY;
446     if (ioctl(s, SIOCDELRT, &rt) < 0)
447         syslog(LOG_WARNING, "default route ioctl(SIOCDELRT): %m");
448 }
449
450 /*
451  * sifproxyarp - Make a proxy ARP entry for the peer.
452  */
453 int
454 sifproxyarp(unit, hisaddr)
455     int unit;
456     u_long hisaddr;
457 {
458     struct arpreq arpreq;
459
460     BZERO(&arpreq, sizeof(arpreq));
461
462     /*
463      * Get the hardware address of an interface on the same subnet
464      * as our local address.
465      */
466     if (!get_ether_addr(hisaddr, &arpreq.arp_ha)) {
467         syslog(LOG_ERR, "Cannot determine ethernet address for proxy ARP");
468         return 0;
469     }
470
471     SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
472     ((struct sockaddr_in *) &arpreq.arp_pa)->sin_addr.s_addr = hisaddr;
473     arpreq.arp_flags = ATF_PERM | ATF_PUBL;
474     if (ioctl(s, SIOCSARP, (caddr_t)&arpreq) < 0) {
475         syslog(LOG_ERR, "ioctl(SIOCSARP): %m");
476         return 0;
477     }
478
479     return 1;
480 }
481
482 /*
483  * cifproxyarp - Delete the proxy ARP entry for the peer.
484  */
485 int
486 cifproxyarp(unit, hisaddr)
487     int unit;
488     u_long hisaddr;
489 {
490     struct arpreq arpreq;
491
492     BZERO(&arpreq, sizeof(arpreq));
493     SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
494     ((struct sockaddr_in *) &arpreq.arp_pa)->sin_addr.s_addr = hisaddr;
495     if (ioctl(s, SIOCDARP, (caddr_t)&arpreq) < 0) {
496         syslog(LOG_WARNING, "ioctl(SIOCDARP): %m");
497         return 0;
498     }
499     return 1;
500 }
501
502 /*
503  * get_ether_addr - get the hardware address of an interface on the
504  * the same subnet as ipaddr.
505  */
506 #define MAX_IFS         32
507
508 int
509 get_ether_addr(ipaddr, hwaddr)
510     u_long ipaddr;
511     struct sockaddr *hwaddr;
512 {
513     struct ifreq *ifr, *ifend, *ifp;
514     u_long ina, mask;
515     struct sockaddr_dl *dla;
516     struct ifreq ifreq;
517     struct ifconf ifc;
518     struct ifreq ifs[MAX_IFS];
519
520     ifc.ifc_len = sizeof(ifs);
521     ifc.ifc_req = ifs;
522     if (ioctl(s, SIOCGIFCONF, &ifc) < 0) {
523         syslog(LOG_ERR, "ioctl(SIOCGIFCONF): %m");
524         return 0;
525     }
526
527     /*
528      * Scan through looking for an interface with an Internet
529      * address on the same subnet as `ipaddr'.
530      */
531     ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
532     for (ifr = ifc.ifc_req; ifr < ifend; ) {
533         if (ifr->ifr_addr.sa_family == AF_INET) {
534             ina = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr;
535             strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
536             /*
537              * Check that the interface is up, and not point-to-point
538              * or loopback.
539              */
540             if (ioctl(s, SIOCGIFFLAGS, &ifreq) < 0)
541                 continue;
542             if ((ifreq.ifr_flags &
543                  (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|IFF_LOOPBACK|IFF_NOARP))
544                  != (IFF_UP|IFF_BROADCAST))
545                 continue;
546             /*
547              * Get its netmask and check that it's on the right subnet.
548              */
549             if (ioctl(s, SIOCGIFNETMASK, &ifreq) < 0)
550                 continue;
551             mask = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr;
552             if ((ipaddr & mask) != (ina & mask))
553                 continue;
554
555             break;
556         }
557         ifr = (struct ifreq *) ((char *)&ifr->ifr_addr + ifr->ifr_addr.sa_len);
558     }
559
560     if (ifr >= ifend)
561         return 0;
562     syslog(LOG_INFO, "found interface %s for proxy arp", ifr->ifr_name);
563
564     /*
565      * Now scan through again looking for a link-level address
566      * for this interface.
567      */
568     ifp = ifr;
569     for (ifr = ifc.ifc_req; ifr < ifend; ) {
570         if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0
571             && ifr->ifr_addr.sa_family == AF_LINK) {
572             /*
573              * Found the link-level address - copy it out
574              */
575             dla = (struct sockaddr_dl *)&ifr->ifr_addr;
576             hwaddr->sa_len = sizeof(struct sockaddr);
577             hwaddr->sa_family = AF_UNSPEC;
578             BCOPY(LLADDR(dla), hwaddr->sa_data, dla->sdl_alen);
579             return 1;
580         }
581         ifr = (struct ifreq *) ((char *)&ifr->ifr_addr + ifr->ifr_addr.sa_len);
582     }
583
584     return 0;
585 }
586
587
588 /*
589  * ppp_available - check whether the system has any ppp interfaces
590  * (in fact we check whether we can do an ioctl on ppp0).
591  */
592 int
593 ppp_available()
594 {
595     int s, ok;
596     struct ifreq ifr;
597
598     if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
599         return 1;               /* can't tell - maybe we're not root */
600
601     strncpy(ifr.ifr_name, "ppp0", sizeof (ifr.ifr_name));
602     ok = ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) >= 0;
603     close(s);
604
605     return ok;
606 }