/* $Header: /usr/cvsroot/target/src/wrn/wm/demo/lib/pppbsd.c,v 1.3 2003/01/15 14:04:33 josh Exp $ */ /* * Copyright (C) 1999-2005 Wind River Systems, Inc. * All rights reserved. Provided under license only. * Distribution or other use of this software is only * permitted pursuant to the terms of a license agreement * from Wind River Systems (and is otherwise prohibited). * Refer to that license agreement for terms of use. */ /**************************************************************************** * Copyright 1993-1997 Epilogue Technology Corporation. * Copyright 1998 Integrated Systems, Inc. * All rights reserved. ****************************************************************************/ /* * $Log: pppbsd.c,v $ * Revision 1.3 2003/01/15 14:04:33 josh * directory structure shifting * * Revision 1.2 2001/11/08 15:56:25 tneale * Updated for newest file layout * * Revision 1.1.1.1 2001/11/05 17:48:42 tneale * Tornado shuffle * * Revision 2.10 2001/01/19 22:23:49 paul * Update copyright. * * Revision 2.9 2000/10/16 19:21:54 paul * Restore sockets and mempool code. * * Revision 2.8 2000/03/17 00:12:43 meister * Update copyright message * * Revision 2.7 2000/03/13 21:22:08 paul * Removed some code that we are no longer working on. * * Revision 2.6 1999/11/05 22:29:06 paul * Updated driver structs to reflect conditional backwards-compatibility * fields and new fields. * * Revision 2.5 1999/03/01 22:05:26 wes * Add Yet Another Way to connect attache ppp to a unix ppp: attach * ourselves to the master side of a pty; run pppd attached to the slave * side. * configured via INSTALL_SNARK_BSD_PPP_OVER_PTY * * Revision 2.4 1998/02/25 15:21:49 sra * Finish moving types.h, bug.h, and bugdef.h to common/h/. * * Revision 2.3 1998/02/25 04:57:35 sra * Update copyrights. * * Revision 2.2 1997/11/18 18:05:19 sra * Make PPP work on Purify/SunOS testbed. * * Revision 2.1 1997/10/28 00:51:55 sra * Convert PPP code to use BUG() macro. * Bang on CHAP and PAP code some more. * Create test driver for PPP under Snark/BSD. * * */ /* [clearcase] modification history ------------------- 01a,19apr05,job update copyright notices */ /* * Point-to-Point Protocol (PPP) interface for snark under BSD-derived systems. * * This code has only been tested under FreeBSD 2.x. It should work on * any BSD-derived system with minor modifications. * * We depend on the PPP media layer code to handle the FCS, address, and * control fields in the framing (PPPC_FCS must be enabled). So all we * have to worry about is the byte stuffing and the framing "flag" byte. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if INSTALL_SNARK_BSD_PPP_OVER_TCP #include #include #include #endif #if INSTALL_SNARK_BSD_PPP_OVER_PTY #include #include #endif #include #include #include #define PF_PPP_BOP 0x1000 #ifndef BSDPPP_COMPORT_NAME #if INSTALL_SNARK_BSD_PPP_OVER_PTY #define BSDPPP_COMPORT_NAME "pty" #elif INSTALL_SNARK_BSD_PPP_OVER_TCP #define BSDPPP_COMPORT_NAME "tcp" #else #define BSDPPP_COMPORT_NAME "cuaa" #endif #endif /* * PPP control structure, hangs off net->specific->private. */ #ifndef SNARKBSD_PPP_IBUFLEN #define SNARKBSD_PPP_IBUFLEN 6000 #endif #ifndef SNARKBSD_PPP_OBUFLEN #define SNARKBSD_PPP_OBUFLEN 6000 #endif struct ppp_driver_ctl { struct net *net; bits32_t snd_asynch_mask; packet *ipkt, *opkt_head, **opkt_tail; int ilen, olen; unsigned char *ibp, *obp; unsigned char ibuf[SNARKBSD_PPP_IBUFLEN], obuf[SNARKBSD_PPP_OBUFLEN]; unsigned iescaped : 1; unsigned idiscard : 1; unsigned oescaped : 1; }; /* * PPP framing and byte stuffing magic */ #define PPP_FLAG 0x7e #define PPP_ESCAPE 0x7d #define PPP_XOR 0x20 #define PPP_MASKED(_c_, _m_) ((_c_) < 32 && ((_m_) & (1 << (_c_)))) #ifndef PPP_DRIVER_RCV_ASYNCH_MASK #define PPP_DRIVER_RCV_ASYNCH_MASK 0 #endif static void hexdump (char *flag, unsigned char *cp, int len) { int i; fprintf(stderr, "%s:", flag); for (i=0; iidiscard) { \ continue; \ } else if (p->pkt_data < p->pkt_buffer + p->pkt_buflen) { \ *p->pkt_data++ = (x); \ } else { \ pdc->idiscard = 1; \ continue; \ } \ } while (0) static packet *ppp_driver_decode (struct net *net, unsigned char *buf, int len, int *used) { struct bsdif *bif = net->specific; struct ppp_driver_ctl *pdc = bif->private; unsigned char *bob = buf, *eob = buf + len; packet *p; while (buf < eob) { if (pdc->ipkt == 0 && (pdc->ipkt = pkt_alloc(net->driver->maxlen)) == 0) break; /* Oops, no packet buffers */ p = pdc->ipkt; if (PPP_MASKED(*buf, PPP_DRIVER_RCV_ASYNCH_MASK)) { ++buf; /* Noise byte, just ignore it */ continue; } if (pdc->iescaped) { /* Was escaped? */ pdc->iescaped = 0; /* ain't no more */ if (*buf == PPP_FLAG) /* End of frame in escape sequence? */ pdc->idiscard = 1; /* yeah, abort this packet */ DOCHAR(*buf++ ^ PPP_XOR); /* Save unescaped byte */ } else { /* Not escaped */ switch (*buf++) { case PPP_FLAG: if (!pdc->idiscard && p->pkt_data > p->pkt_buffer) { pdc->ipkt = 0; p->pkt_datalen = p->pkt_data - p->pkt_buffer; p->pkt_data = p->pkt_buffer; if (used) *used = buf - bob; return p; /* Happy packet */ } pdc->idiscard = 0; /* Bogus packet now history */ p->pkt_data = p->pkt_buffer; continue; case PPP_ESCAPE: pdc->iescaped = 1; /* An escaping byte! */ continue; default: DOCHAR(*(buf-1)); /* A normal boring byte */ continue; } } } if (used) *used = buf - bob; return 0; /* Yes, we have no packet today */ } #undef DOCHAR /* * Encode zero or more Attache packet buffers into a data buffer. * Packets will have been queued for us by ppp_driver_send(). */ #define DOCHAR(x) \ do { *buf++ = (x); if (buf == eob) return (buf - bob); } while (0) static int ppp_driver_encode(struct net *net, unsigned char *buf, int len) { struct bsdif *bif = net->specific; struct ppp_driver_ctl *pdc = bif->private; unsigned char *bob = buf, *eob = buf + len; packet *p; if (buf >= eob) return 0; while ((p = pdc->opkt_head) != 0) { /* * Send framing sequence before a new packet, to flush line noise. */ if ((p->pkt_flags & PF_PPP_BOP) != 0) { p->pkt_flags &= ~PF_PPP_BOP; DOCHAR(PPP_FLAG); } /* * Process data in this packet. */ while (p->pkt_datalen > 0) { if (pdc->oescaped) { pdc->oescaped = 0; /* Was escaped, ain't no more */ DOCHAR((*p->pkt_data++) ^ PPP_XOR); } else { switch (*p->pkt_data) { default: if (!PPP_MASKED(*p->pkt_data, pdc->snd_asynch_mask)) { DOCHAR(*p->pkt_data++); break; /* Ordinary char, just send it */ } case PPP_FLAG: /* I need an escape */ case PPP_ESCAPE: pdc->oescaped = 1; DOCHAR(PPP_ESCAPE); continue; /* Doesn't count as sent yet */ } } p->pkt_datalen--; /* One less octet to feed */ } /* * No more data in current packet, so send framing sequence * and proceed with next packet in the output queue. */ DOCHAR(PPP_FLAG); if ((pdc->opkt_head = p->pkt_link) == 0) pdc->opkt_tail = &pdc->opkt_head; pkt_free(p); } return buf - bob; } #undef DOCHAR /* * Process whatever incoming PPP data we can without blocking, * passing zero or more Attache packets to ppp_rcv(). */ static void ppp_driver_input(struct net *net) { struct bsdif *bif = net->specific; struct ppp_driver_ctl *pdc = bif->private; packet *p; int n; if (pdc->ilen <= 0) { do { n = read(bif->fd, (char *) pdc->ibuf, sizeof(pdc->ibuf)); } while (n < 0 && errno == EINTR); if (n == 0 || (n < 0 && errno != EWOULDBLOCK)) { bif->flags &= ~BSDIF_READ; return; } pdc->ilen = n; pdc->ibp = pdc->ibuf; } while (pdc->ilen > 0) { if ((p = ppp_driver_decode(net, pdc->ibp, pdc->ilen, &n)) != 0) { p->pkt_n = net; hexdump("RX", p->pkt_data, p->pkt_datalen); ppp_rcv(p); } if (n == 0) /* out of packet buffers, try again later */ break; pdc->ibp += n; /* count what we consumed */ pdc->ilen -= n; } } /* * Process whatever outgoing Attache packets we can without blocking. * Generates zero or more writes of encoded PPP data. */ static void ppp_driver_output(struct net *net) { struct bsdif *bif = net->specific; struct ppp_driver_ctl *pdc = bif->private; int n; for (;;) { if (pdc->olen == 0) { if ((n = ppp_driver_encode(net, pdc->obuf, sizeof(pdc->obuf))) == 0) break; pdc->olen = n; pdc->obp = pdc->obuf; } do { n = write(bif->fd, pdc->obp, pdc->olen); } while (n < 0 && errno == EINTR); if (n <= 0) break; pdc->obp += n; pdc->olen -= n; } if (n == 0 || (n < 0 && errno != EWOULDBLOCK)) bif->flags &= ~BSDIF_WRITE; } /* * Handle events detected by generic NetBSD I/O loop. * The flags tell us what kind of event happened, we call the * non-blocking I/O routines to do the real work. */ static void ppp_driver_handler(int fd, void *net, unsigned flags) { if ((flags & BSDIF_WRITE) != 0) ppp_driver_output(net); if ((flags & BSDIF_READ) != 0) ppp_driver_input(net); } /* * Attache device driver routines. */ static void ppp_driver_send(packet *p) { struct net *net = p->pkt_n; struct bsdif *bif = net->specific; struct ppp_driver_ctl *pdc = bif->private; hexdump("TX", p->pkt_data, p->pkt_datalen); p->pkt_flags |= PF_PPP_BOP; /* Flag that this is a new packet */ p->pkt_link = 0; /* Stuff this packet into the */ *pdc->opkt_tail = p; /* output queue */ pdc->opkt_tail = &p->pkt_link; bif->flags |= BSDIF_WRITE; /* Now interested in output events */ ppp_driver_output(net); /* Send what we can right away */ } static void ppp_driver_close(struct net *net) { struct bsdif *bif = net->specific; net->flags |= NF_DRIVER_DOWN; bif->flags &= ~(BSDIF_READ | BSDIF_WRITE); if (bif->fd >= 0) { (void) close(bif->fd); /* Clean up our file if we had one */ bif->fd = -1; } if (bif->private) { GLUE_FREE(bif->private); /* Release our memory if we had any */ bif->private = 0; } } static void ppp_driver_init(struct net *net) { struct bsdif *bif = net->specific; struct ppp_driver_ctl *pdc; char name[64]; net->flags |= NF_DRIVER_DOWN; if (!net->speed) net->speed = 115200; if ((bif->private = pdc = GLUE_ALLOC(sizeof(*pdc))) == 0) { fprintf(stderr, "couldn't allocate ppp control structure\n"); return; } MEMSET(pdc, 0, sizeof(*pdc)); pdc->opkt_tail = &pdc->opkt_head; pdc->net = net; #if INSTALL_SNARK_BSD_PPP_OVER_PTY { int pty, tty; int subpid; char *pppd_path; struct termios t; int nsleep = 10; sprintf(name, "SNARKBSDPPP_PTY%d", net->instance); pppd_path = getenv(name); if (!pppd_path) return; if (openpty(&pty, &tty, name, NULL, NULL) < 0) { perror("can't open pty"); return; } tcgetattr(tty, &t); cfmakeraw(&t); tcsetattr(tty, TCSAFLUSH, &t); fflush(NULL); subpid = fork(); if (subpid < 0) { perror("fork"); return; /* !!! cleanup? */ } if (subpid == 0) { /* in the child; start pppd... */ dup2(tty, 0); dup2(tty, 1); close(pty); close(tty); fprintf(stderr, "execing %s in pid %d on %s in %d seconds...\n", pppd_path, getpid(), name, nsleep); sleep(nsleep); fprintf(stderr, "execing now\n"); execlp(pppd_path, "pppd", name, "file", "snark.ppprc", NULL); _exit(1); } /* now in the parent.. */ close(tty); bif->fd = pty; sleep(nsleep+1); } #elif !INSTALL_SNARK_BSD_PPP_OVER_TCP /* * Normal /dev/ttyxxx-based PPP. This code was written for FreeBSD 2.2.1, * it should be pretty easy to make it work on other Unix-like systems. */ { struct termios termios; sprintf(name, "/dev/%s", net->s_name + 3); if ((bif->fd = open(name, O_RDWR, 0660)) < 0) { /* |O_NONBLOCK */ perror("couldn't open ppp device"); goto lose; } if (tcgetattr(bif->fd, &termios) < 0) { perror("couldn't get ppp line state"); goto lose; } if (cfmakeraw(&termios), cfsetspeed(&termios, net->speed) < 0) { perror("couldn't fiddle ppp state"); goto lose; } if (tcsetattr(bif->fd, TCSANOW, &termios) < 0) { perror("couldn't set ppp line flags and speed"); goto lose; } } #elif 0 /* * PPP over TCP. This version does an active connect. In theory, * it should work nicely with IIJ-PPP in -direct mode running under * Juergen Nickelsen's "socket" program, but in practice the connection * keeps getting closed before LCP ever really gets going. Sigh. */ { struct sockaddr_in sin; char host[20]; unsigned short port; sprintf(name, "SNARKBSDPPP_%s", net->s_name + 3); if (sscanf(getenv(name), "%16[0-9.]:%hu", host, &port) != 2) { fprintf(stderr, "couldn't line information for PPP/TCP link\n"); goto lose; } MEMSET(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(port); sin.sin_addr.s_addr = inet_addr(host); if ((bif->fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("couldn't get socket for PPP/TCP link"); goto lose; } if (connect(bif->fd, (struct sockaddr *) &sin, sizeof(sin)) < 0) { perror("couldn't connect PPP/TCP link"); goto lose; } } #else /* * PPP over TCP. This version does a passive listen, which, in theory, * should allow IIJ-PPP to connect to it directly. */ { struct sockaddr_in sin; unsigned short port; int s, n; sprintf(name, "SNARKBSDPPP_%s", net->s_name + 3); if (sscanf(getenv(name), "%hu", &port) != 1) { fprintf(stderr, "couldn't parse line information for PPP/TCP link %s: %s\n", name, getenv(name)); goto lose; } MEMSET(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(port); if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("couldn't get socket for PPP/TCP listener"); goto lose; } if (bind(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) { perror("couldn't bind socket for PPP/TCP listener"); goto lose; } if (listen(s, 1) < 0) { perror("couldn't listen for for PPP/TCP connection"); goto lose; } fprintf(stderr, "[Listening for PPP/TCP connection on port %u]\n", port); n = sizeof(sin); if ((bif->fd = accept(s, (struct sockaddr *) &sin, &n)) < 0) { perror("couldn't accept PPP/TCP connection"); goto lose; } close(s); fprintf(stderr, "[Got PPP/TCP connection from %s:%u]\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); } #endif if (fcntl(bif->fd, F_SETFL, FNDELAY) < 0) { perror("unable to set ppp line non-blocking"); goto lose; } bif->handler = ppp_driver_handler; bif->flags |= BSDIF_READ; net->flags &= ~NF_DRIVER_DOWN; return; /* success */ lose: ppp_driver_close(net); /* failure */ } static int ppp_driver_device_ctl(struct net *net, int code, ptr_t cookie) { struct bsdif *bif = net->specific; struct ppp_driver_ctl *pdc = bif->private; switch (code) { case DEVICE_CTL_SET_PPP_ASYNC_CTL_MAP: BUG_ASSERT(cookie && bif && pdc); pdc->snd_asynch_mask = *(bits32_t *)cookie; return DEVICE_CTL_GOOD; case DEVICE_CTL_GET_PPP_RCV_ASYNC_CTL_MAP: BUG_ASSERT(cookie); *(bits32_t *)cookie = PPP_DRIVER_RCV_ASYNCH_MASK; return DEVICE_CTL_GOOD; default: return DEVICE_CTL_UNKNOWN_CODE; } } static struct driver ppp_driver = { ppp_driver_init, /* Init routine */ ppp_driver_send, /* Raw send routine */ ppp_ip_send, /* IP packet send routine */ 0, /* ARP routine */ ppp_driver_device_ctl, /* Device control routine */ ppp_driver_close, /* Close routine */ "ppp", /* Driver name prefix */ "Point-to-Point Protocol", /* Driver name */ 4, /* Header length (adr, ctl, prot) */ 2, /* Trailer length (FCS) */ 1506, /* Max packet size */ IF_PPP, /* MIB interface type */ 0, /* ARP hardware type */ ppp_control, /* Media control routine */ 0, /* IPv6 send */ 0 /* IPv4 send */ }; void ppp_driver_find (void (*config)(char *, struct driver *, int, bits16_t, unsigned, bits32_t)) { char name[64]; int speed, instance; for (instance = 0; ; instance++) { #if INSTALL_SNARK_BSD_PPP_OVER_PTY sprintf(name, "SNARKBSDPPP_PTY%d", instance); if (!getenv(name)) return; speed = 115200; #elif INSTALL_SNARK_BSD_PPP_OVER_TCP sprintf(name, "SNARKBSDPPP_%s%d", BSDPPP_COMPORT_NAME, instance); if (!getenv(name)) return; speed = 115200; #else struct termios termios; int fd; sprintf(name, "/dev/%s%d", BSDPPP_COMPORT_NAME, instance); if ((fd = open(name, O_RDONLY | O_NONBLOCK)) < 0) { if (errno == ENOENT) return; continue; } speed = (tcgetattr(fd, &termios) < 0 ? 0 : cfgetispeed(&termios)); (void) close(fd); if (speed == 0) continue; #endif sprintf(name, "%s%d", BSDPPP_COMPORT_NAME, instance); config(name, &ppp_driver, instance, 0, ppp_driver.maxlen - ppp_driver.lnh - ppp_driver.lnt, speed); } }