]> 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.4 1994/04/18 04:09:27 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
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 {
286     struct ifreq ifr;
287     u_int x;
288
289     strncpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
290     if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
291         syslog(LOG_ERR, "ioctl (SIOCGIFFLAGS): %m");
292         return 0;
293     }
294     ifr.ifr_flags |= IFF_UP;
295     if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
296         syslog(LOG_ERR, "ioctl(SIOCSIFFLAGS): %m");
297         return 0;
298     }
299     if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) {
300         syslog(LOG_ERR, "ioctl (PPPIOCGFLAGS): %m");
301         return 0;
302     }
303     x |= SC_ENABLE_IP;
304     if (ioctl(fd, PPPIOCSFLAGS, (caddr_t) &x) < 0) {
305         syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m");
306         return 0;
307     }
308     return 1;
309 }
310
311 /*
312  * sifdown - Config the interface down and disable IP.
313  */
314 int
315 sifdown(u)
316 {
317     struct ifreq ifr;
318     u_int x;
319     int rv;
320
321     rv = 1;
322     if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) {
323         syslog(LOG_ERR, "ioctl (PPPIOCGFLAGS): %m");
324         rv = 0;
325     } else {
326         x &= ~SC_ENABLE_IP;
327         if (ioctl(fd, PPPIOCSFLAGS, (caddr_t) &x) < 0) {
328             syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m");
329             rv = 0;
330         }
331     }
332     strncpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
333     if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
334         syslog(LOG_ERR, "ioctl (SIOCGIFFLAGS): %m");
335         rv = 0;
336     } else {
337         ifr.ifr_flags &= ~IFF_UP;
338         if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
339             syslog(LOG_ERR, "ioctl(SIOCSIFFLAGS): %m");
340             rv = 0;
341         }
342     }
343     return rv;
344 }
345
346 /*
347  * SET_SA_FAMILY - set the sa_family field of a struct sockaddr,
348  * if it exists.
349  */
350 #define SET_SA_FAMILY(addr, family)             \
351     BZERO((char *) &(addr), sizeof(addr));      \
352     addr.sa_family = (family);                  \
353     addr.sa_len = sizeof(addr);
354
355 /*
356  * sifaddr - Config the interface IP addresses and netmask.
357  */
358 int
359 sifaddr(u, o, h, m)
360 {
361     struct ifaliasreq ifra;
362
363     strncpy(ifra.ifra_name, ifname, sizeof(ifra.ifra_name));
364     SET_SA_FAMILY(ifra.ifra_addr, AF_INET);
365     ((struct sockaddr_in *) &ifra.ifra_addr)->sin_addr.s_addr = o;
366     SET_SA_FAMILY(ifra.ifra_broadaddr, AF_INET);
367     ((struct sockaddr_in *) &ifra.ifra_broadaddr)->sin_addr.s_addr = h;
368     if (m != 0) {
369         SET_SA_FAMILY(ifra.ifra_mask, AF_INET);
370         ((struct sockaddr_in *) &ifra.ifra_mask)->sin_addr.s_addr = m;
371     } else
372         BZERO(&ifra.ifra_mask, sizeof(ifra.ifra_mask));
373     if (ioctl(s, SIOCAIFADDR, (caddr_t) &ifra) < 0) {
374         if (errno != EEXIST) {
375             syslog(LOG_ERR, "ioctl(SIOCAIFADDR): %m");
376             return 0;
377         }
378         syslog(LOG_WARNING, "ioctl(SIOCAIFADDR): Address already exists");
379     }
380     return 1;
381 }
382
383 /*
384  * cifaddr - Clear the interface IP addresses, and delete routes
385  * through the interface if possible.
386  */
387 int
388 cifaddr(u, o, h)
389 {
390     struct ifaliasreq ifra;
391
392     strncpy(ifra.ifra_name, ifname, sizeof(ifra.ifra_name));
393     SET_SA_FAMILY(ifra.ifra_addr, AF_INET);
394     ((struct sockaddr_in *) &ifra.ifra_addr)->sin_addr.s_addr = o;
395     SET_SA_FAMILY(ifra.ifra_broadaddr, AF_INET);
396     ((struct sockaddr_in *) &ifra.ifra_broadaddr)->sin_addr.s_addr = h;
397     BZERO(&ifra.ifra_mask, sizeof(ifra.ifra_mask));
398     if (ioctl(s, SIOCDIFADDR, (caddr_t) &ifra) < 0) {
399         syslog(LOG_WARNING, "ioctl(SIOCDIFADDR): %m");
400         return 0;
401     }
402     return 1;
403 }
404
405 /*
406  * sifdefaultroute - assign a default route through the address given.
407  */
408 int
409 sifdefaultroute(u, g)
410 {
411     struct ortentry rt;
412
413     SET_SA_FAMILY(rt.rt_dst, AF_INET);
414     SET_SA_FAMILY(rt.rt_gateway, AF_INET);
415     ((struct sockaddr_in *) &rt.rt_gateway)->sin_addr.s_addr = g;
416     rt.rt_flags = RTF_GATEWAY;
417     if (ioctl(s, SIOCADDRT, &rt) < 0) {
418         syslog(LOG_ERR, "default route ioctl(SIOCADDRT): %m");
419         return 0;
420     }
421     return 1;
422 }
423
424 /*
425  * cifdefaultroute - delete a default route through the address given.
426  */
427 int
428 cifdefaultroute(u, g)
429 {
430     struct ortentry rt;
431
432     SET_SA_FAMILY(rt.rt_dst, AF_INET);
433     SET_SA_FAMILY(rt.rt_gateway, AF_INET);
434     ((struct sockaddr_in *) &rt.rt_gateway)->sin_addr.s_addr = g;
435     rt.rt_flags = RTF_GATEWAY;
436     if (ioctl(s, SIOCDELRT, &rt) < 0)
437         syslog(LOG_WARNING, "default route ioctl(SIOCDELRT): %m");
438 }
439
440 /*
441  * sifproxyarp - Make a proxy ARP entry for the peer.
442  */
443 int
444 sifproxyarp(unit, hisaddr)
445     int unit;
446     u_long hisaddr;
447 {
448     struct arpreq arpreq;
449
450     BZERO(&arpreq, sizeof(arpreq));
451
452     /*
453      * Get the hardware address of an interface on the same subnet
454      * as our local address.
455      */
456     if (!get_ether_addr(hisaddr, &arpreq.arp_ha)) {
457         syslog(LOG_ERR, "Cannot determine ethernet address for proxy ARP");
458         return 0;
459     }
460
461     SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
462     ((struct sockaddr_in *) &arpreq.arp_pa)->sin_addr.s_addr = hisaddr;
463     arpreq.arp_flags = ATF_PERM | ATF_PUBL;
464     if (ioctl(s, SIOCSARP, (caddr_t)&arpreq) < 0) {
465         syslog(LOG_ERR, "ioctl(SIOCSARP): %m");
466         return 0;
467     }
468
469     return 1;
470 }
471
472 /*
473  * cifproxyarp - Delete the proxy ARP entry for the peer.
474  */
475 int
476 cifproxyarp(unit, hisaddr)
477     int unit;
478     u_long hisaddr;
479 {
480     struct arpreq arpreq;
481
482     BZERO(&arpreq, sizeof(arpreq));
483     SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
484     ((struct sockaddr_in *) &arpreq.arp_pa)->sin_addr.s_addr = hisaddr;
485     if (ioctl(s, SIOCDARP, (caddr_t)&arpreq) < 0) {
486         syslog(LOG_WARNING, "ioctl(SIOCDARP): %m");
487         return 0;
488     }
489     return 1;
490 }
491
492 /*
493  * get_ether_addr - get the hardware address of an interface on the
494  * the same subnet as ipaddr.
495  */
496 #define MAX_IFS         32
497
498 int
499 get_ether_addr(ipaddr, hwaddr)
500     u_long ipaddr;
501     struct sockaddr *hwaddr;
502 {
503     struct ifreq *ifr, *ifend, *ifp;
504     u_long ina, mask;
505     struct sockaddr_dl *dla;
506     struct ifreq ifreq;
507     struct ifconf ifc;
508     struct ifreq ifs[MAX_IFS];
509
510     ifc.ifc_len = sizeof(ifs);
511     ifc.ifc_req = ifs;
512     if (ioctl(s, SIOCGIFCONF, &ifc) < 0) {
513         syslog(LOG_ERR, "ioctl(SIOCGIFCONF): %m");
514         return 0;
515     }
516
517     /*
518      * Scan through looking for an interface with an Internet
519      * address on the same subnet as `ipaddr'.
520      */
521     ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
522     for (ifr = ifc.ifc_req; ifr < ifend; ) {
523         if (ifr->ifr_addr.sa_family == AF_INET) {
524             ina = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr;
525             strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
526             /*
527              * Check that the interface is up, and not point-to-point
528              * or loopback.
529              */
530             if (ioctl(s, SIOCGIFFLAGS, &ifreq) < 0)
531                 continue;
532             if ((ifreq.ifr_flags &
533                  (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|IFF_LOOPBACK|IFF_NOARP))
534                  != (IFF_UP|IFF_BROADCAST))
535                 continue;
536             /*
537              * Get its netmask and check that it's on the right subnet.
538              */
539             if (ioctl(s, SIOCGIFNETMASK, &ifreq) < 0)
540                 continue;
541             mask = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr;
542             if ((ipaddr & mask) != (ina & mask))
543                 continue;
544
545             break;
546         }
547         ifr = (struct ifreq *) ((char *)&ifr->ifr_addr + ifr->ifr_addr.sa_len);
548     }
549
550     if (ifr >= ifend)
551         return 0;
552     syslog(LOG_INFO, "found interface %s for proxy arp", ifr->ifr_name);
553
554     /*
555      * Now scan through again looking for a link-level address
556      * for this interface.
557      */
558     ifp = ifr;
559     for (ifr = ifc.ifc_req; ifr < ifend; ) {
560         if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0
561             && ifr->ifr_addr.sa_family == AF_LINK) {
562             /*
563              * Found the link-level address - copy it out
564              */
565             dla = (struct sockaddr_dl *)&ifr->ifr_addr;
566             hwaddr->sa_len = sizeof(struct sockaddr);
567             hwaddr->sa_family = AF_UNSPEC;
568             BCOPY(LLADDR(dla), hwaddr->sa_data, dla->sdl_alen);
569             return 1;
570         }
571         ifr = (struct ifreq *) ((char *)&ifr->ifr_addr + ifr->ifr_addr.sa_len);
572     }
573
574     return 0;
575 }
576
577
578 /*
579  * ppp_available - check whether the system has any ppp interfaces
580  * (in fact we check whether we can do an ioctl on ppp0).
581  */
582 int
583 ppp_available()
584 {
585     int s, ok;
586     struct ifreq ifr;
587
588     if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
589         return 1;               /* can't tell - maybe we're not root */
590
591     strncpy(ifr.ifr_name, "ppp0", sizeof (ifr.ifr_name));
592     ok = ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) >= 0;
593     close(s);
594
595     return ok;
596 }