X-Git-Url: http://git.ozlabs.org/?p=ppp.git;a=blobdiff_plain;f=pppd%2Fsys-svr4.c;h=8f1845370dfd995e46383c6f967e5425db1aba0d;hp=5f0ffa573689c80a0ddff1aaa2299998cc67e0dd;hb=66b97cb2741ff687e731834eb6458281df3dd412;hpb=cbc5d9b19c0a4e6546e5d9e32240249d41b03098 diff --git a/pppd/sys-svr4.c b/pppd/sys-svr4.c index 5f0ffa5..8f18453 100644 --- a/pppd/sys-svr4.c +++ b/pppd/sys-svr4.c @@ -1,31 +1,96 @@ /* * System-dependent procedures for pppd under Solaris 2. * - * Copyright (c) 1994 The Australian National University. + * Parts re-written by Adi Masputra , based on + * the original sys-svr4.c + * + * Copyright (c) 2000 by Sun Microsystems, Inc. * All rights reserved. * * Permission to use, copy, modify, and distribute this software and its * documentation is hereby granted, provided that the above copyright - * notice appears in all copies. This software is provided without any - * warranty, express or implied. The Australian National University - * makes no representations about the suitability of this software for - * any purpose. + * notice appears in all copies. + * + * SUN MAKES NO REPRESENTATION OR WARRANTIES ABOUT THE SUITABILITY OF + * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES + * + * Copyright (c) 1995-2002 Paul Mackerras. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The name(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Paul Mackerras + * ". + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Derived from main.c and pppd.h, which are: + * + * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. * - * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY - * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES - * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF - * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY - * OF SUCH DAMAGE. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. * - * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO - * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, - * OR MODIFICATIONS. + * 3. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any legal + * details, please contact + * Office of Technology Transfer + * Carnegie Mellon University + * 5000 Forbes Avenue + * Pittsburgh, PA 15213-3890 + * (412) 268-4387, fax: (412) 268-7395 + * tech-transfer@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#define RCSID "$Id: sys-svr4.c,v 1.34 1999/09/08 01:11:16 masputra Exp $" +#define RCSID "$Id: sys-svr4.c,v 1.45 2002/12/04 23:03:33 paulus Exp $" #include #include @@ -63,17 +128,90 @@ #include #include #include +#include #endif #include "pppd.h" +#include "fsm.h" +#include "lcp.h" +#include "ipcp.h" +#include "ccp.h" + +#if !defined(PPP_DEV_NAME) +#define PPP_DEV_NAME "/dev/ppp" +#endif /* !defined(PPP_DEV_NAME) */ + +#if !defined(AHDLC_MOD_NAME) +#define AHDLC_MOD_NAME "ppp_ahdl" +#endif /* !defined(AHDLC_MOD_NAME) */ + +#if !defined(COMP_MOD_NAME) +#define COMP_MOD_NAME "ppp_comp" +#endif /* !defined(COMP_MOD_NAME) */ + +#if !defined(IP_DEV_NAME) +#define IP_DEV_NAME "/dev/ip" +#endif /* !defined(IP_DEV_NAME) */ + +#if !defined(IP_MOD_NAME) +#define IP_MOD_NAME "ip" +#endif /* !defined(IP_MOD_NAME) */ + +#if !defined(UDP_DEV_NAME) && defined(SOL2) +#define UDP_DEV_NAME "/dev/udp" +#endif /* !defined(UDP_DEV_NAME) && defined(SOL2) */ + +#if !defined(UDP6_DEV_NAME) && defined(SOL2) +#define UDP6_DEV_NAME "/dev/udp6" +#endif /* !defined(UDP6_DEV_NAME) && defined(SOL2) */ static const char rcsid[] = RCSID; +#if defined(SOL2) +/* + * "/dev/udp" is used as a multiplexor to PLINK the interface stream + * under. It is used in place of "/dev/ip" since STREAMS will not let + * a driver be PLINK'ed under itself, and "/dev/ip" is typically the + * driver at the bottom of the tunneling interfaces stream. + */ +static char *mux_dev_name = UDP_DEV_NAME; +#else +static char *mux_dev_name = IP_DEV_NAME; +#endif static int pppfd; static int fdmuxid = -1; static int ipfd; static int ipmuxid = -1; +#if defined(INET6) && defined(SOL2) +static int ip6fd; /* IP file descriptor */ +static int ip6muxid = -1; /* Multiplexer file descriptor */ +static int if6_is_up = 0; /* IPv6 interface has been marked up */ + +#define _IN6_LLX_FROM_EUI64(l, s, eui64, as) do { \ + s->sin6_addr.s6_addr32[0] = htonl(as); \ + eui64_copy(eui64, s->sin6_addr.s6_addr32[2]); \ + s->sin6_family = AF_INET6; \ + l.lifr_addr.ss_family = AF_INET6; \ + l.lifr_addrlen = 10; \ + l.lifr_addr = laddr; \ + } while (0) + +#define IN6_LLADDR_FROM_EUI64(l, s, eui64) \ + _IN6_LLX_FROM_EUI64(l, s, eui64, 0xfe800000) + +#define IN6_LLTOKEN_FROM_EUI64(l, s, eui64) \ + _IN6_LLX_FROM_EUI64(l, s, eui64, 0) + +#endif /* defined(INET6) && defined(SOL2) */ + +#if defined(INET6) && defined(SOL2) +static char first_ether_name[LIFNAMSIZ]; /* Solaris 8 and above */ +#else +static char first_ether_name[IFNAMSIZ]; /* Before Solaris 8 */ +#define MAXIFS 256 /* Max # of interfaces */ +#endif /* defined(INET6) && defined(SOL2) */ + static int restore_term; static struct termios inittermios; #ifndef CRTSCTS @@ -106,6 +244,7 @@ static int translate_speed __P((int)); static int baud_rate_of __P((int)); static int get_ether_addr __P((u_int32_t, struct sockaddr *)); static int get_hw_addr __P((char *, u_int32_t, struct sockaddr *)); +static int get_hw_addr_dlpi __P((char *, struct sockaddr *)); static int dlpi_attach __P((int, int)); static int dlpi_info_req __P((int)); static int dlpi_get_reply __P((int, union DL_primitives *, int, int)); @@ -132,17 +271,300 @@ static int strioctl __P((int, int, void *, int, int)); */ static int sifppa(fd, ppa) - int fd; - int ppa; + int fd; + int ppa; { - if (ioctl(fd, IF_UNITSEL, (char *)&ppa) < 0) { - return 0; - } + return (int)ioctl(fd, IF_UNITSEL, (char *)&ppa); +} +#endif /* SOL2 */ + +#if defined(SOL2) && defined(INET6) +/* + * get_first_ethernet - returns the first Ethernet interface name found in + * the system, or NULL if none is found + * + * NOTE: This is the lifreq version (Solaris 8 and above) + */ +char * +get_first_ethernet() +{ + struct lifnum lifn; + struct lifconf lifc; + struct lifreq *plifreq; + struct lifreq lifr; + int fd, num_ifs, i, found; + uint_t fl, req_size; + char *req; + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + return 0; + } + + /* + * Find out how many interfaces are running + */ + lifn.lifn_family = AF_UNSPEC; + lifn.lifn_flags = LIFC_NOXMIT; + if (ioctl(fd, SIOCGLIFNUM, &lifn) < 0) { + close(fd); + error("could not determine number of interfaces: %m"); + return 0; + } + + num_ifs = lifn.lifn_count; + req_size = num_ifs * sizeof(struct lifreq); + req = malloc(req_size); + if (req == NULL) { + close(fd); + error("out of memory"); + return 0; + } + + /* + * Get interface configuration info for all interfaces + */ + lifc.lifc_family = AF_UNSPEC; + lifc.lifc_flags = LIFC_NOXMIT; + lifc.lifc_len = req_size; + lifc.lifc_buf = req; + if (ioctl(fd, SIOCGLIFCONF, &lifc) < 0) { + close(fd); + free(req); + error("SIOCGLIFCONF: %m"); + return 0; + } + + /* + * And traverse each interface to look specifically for the first + * occurence of an Ethernet interface which has been marked up + */ + plifreq = lifc.lifc_req; + found = 0; + for (i = lifc.lifc_len / sizeof(struct lifreq); i > 0; i--, plifreq++) { + + if (strchr(plifreq->lifr_name, ':') != NULL) + continue; + + memset(&lifr, 0, sizeof(lifr)); + strncpy(lifr.lifr_name, plifreq->lifr_name, sizeof(lifr.lifr_name)); + if (ioctl(fd, SIOCGLIFFLAGS, &lifr) < 0) { + close(fd); + free(req); + error("SIOCGLIFFLAGS: %m"); + return 0; + } + fl = lifr.lifr_flags; + + if ((fl & (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|IFF_LOOPBACK|IFF_NOARP)) + != (IFF_UP | IFF_BROADCAST)) + continue; + + found = 1; + break; + } + free(req); + close(fd); + + if (found) { + strncpy(first_ether_name, lifr.lifr_name, sizeof(first_ether_name)); + return (char *)first_ether_name; + } else + return NULL; +} +#else +/* + * get_first_ethernet - returns the first Ethernet interface name found in + * the system, or NULL if none is found + * + * NOTE: This is the ifreq version (before Solaris 8). + */ +char * +get_first_ethernet() +{ + struct ifconf ifc; + struct ifreq *pifreq; + struct ifreq ifr; + int fd, num_ifs, i, found; + uint_t fl, req_size; + char *req; + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + return 0; + } + + /* + * Find out how many interfaces are running + */ + if (ioctl(fd, SIOCGIFNUM, (char *)&num_ifs) < 0) { + num_ifs = MAXIFS; + } + + req_size = num_ifs * sizeof(struct ifreq); + req = malloc(req_size); + if (req == NULL) { + close(fd); + error("out of memory"); + return 0; + } + + /* + * Get interface configuration info for all interfaces + */ + ifc.ifc_len = req_size; + ifc.ifc_buf = req; + if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) { + close(fd); + free(req); + error("SIOCGIFCONF: %m"); + return 0; + } + + /* + * And traverse each interface to look specifically for the first + * occurence of an Ethernet interface which has been marked up + */ + pifreq = ifc.ifc_req; + found = 0; + for (i = ifc.ifc_len / sizeof(struct ifreq); i > 0; i--, pifreq++) { + + if (strchr(pifreq->ifr_name, ':') != NULL) + continue; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, pifreq->ifr_name, sizeof(ifr.ifr_name)); + if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) { + close(fd); + free(req); + error("SIOCGIFFLAGS: %m"); + return 0; + } + fl = ifr.ifr_flags; + + if ((fl & (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|IFF_LOOPBACK|IFF_NOARP)) + != (IFF_UP | IFF_BROADCAST)) + continue; + + found = 1; + break; + } + free(req); + close(fd); + + if (found) { + strncpy(first_ether_name, ifr.ifr_name, sizeof(first_ether_name)); + return (char *)first_ether_name; + } else + return NULL; +} +#endif /* defined(SOL2) && defined(INET6) */ + +#if defined(SOL2) +/* + * get_if_hwaddr - get the hardware address for the specified + * network interface device. + */ +int +get_if_hwaddr(u_char *addr, char *if_name) +{ + struct sockaddr s_eth_addr; + struct ether_addr *eth_addr = (struct ether_addr *)&s_eth_addr.sa_data; + + if (if_name == NULL) + return -1; - return 1; + /* + * Send DL_INFO_REQ to the driver to solicit its MAC address + */ + if (!get_hw_addr_dlpi(if_name, &s_eth_addr)) { + error("could not obtain hardware address for %s", if_name); + return -1; + } + + memcpy(addr, eth_addr->ether_addr_octet, 6); + return 1; } #endif /* SOL2 */ +#if defined(SOL2) && defined(INET6) +/* + * slifname - Sets interface ppa and flags + * + * in addition to the comments stated in sifppa(), IFF_IPV6 bit must + * be set in order to declare this as an IPv6 interface + */ +static int +slifname(fd, ppa) + int fd; + int ppa; +{ + struct lifreq lifr; + int ret; + + memset(&lifr, 0, sizeof(lifr)); + ret = ioctl(fd, SIOCGLIFFLAGS, &lifr); + if (ret < 0) + goto slifname_done; + + lifr.lifr_flags |= IFF_IPV6; + lifr.lifr_flags &= ~(IFF_BROADCAST | IFF_IPV4); + lifr.lifr_ppa = ppa; + strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name)); + + ret = ioctl(fd, SIOCSLIFNAME, &lifr); + +slifname_done: + return ret; + + +} + + +/* + * ether_to_eui64 - Convert 48-bit Ethernet address into 64-bit EUI + * + * walks the list of valid ethernet interfaces, and convert the first + * found 48-bit MAC address into EUI 64. caller also assumes that + * the system has a properly configured Ethernet interface for this + * function to return non-zero. + */ +int +ether_to_eui64(eui64_t *p_eui64) +{ + struct sockaddr s_eth_addr; + struct ether_addr *eth_addr = (struct ether_addr *)&s_eth_addr.sa_data; + char *if_name; + + if ((if_name = get_first_ethernet()) == NULL) { + error("no persistent id can be found"); + return 0; + } + + /* + * Send DL_INFO_REQ to the driver to solicit its MAC address + */ + if (!get_hw_addr_dlpi(if_name, &s_eth_addr)) { + error("could not obtain hardware address for %s", if_name); + return 0; + } + + /* + * And convert the EUI-48 into EUI-64, per RFC 2472 [sec 4.1] + */ + p_eui64->e8[0] = (eth_addr->ether_addr_octet[0] & 0xFF) | 0x02; + p_eui64->e8[1] = (eth_addr->ether_addr_octet[1] & 0xFF); + p_eui64->e8[2] = (eth_addr->ether_addr_octet[2] & 0xFF); + p_eui64->e8[3] = 0xFF; + p_eui64->e8[4] = 0xFE; + p_eui64->e8[5] = (eth_addr->ether_addr_octet[3] & 0xFF); + p_eui64->e8[6] = (eth_addr->ether_addr_octet[4] & 0xFF); + p_eui64->e8[7] = (eth_addr->ether_addr_octet[5] & 0xFF); + + return 1; +} +#endif /* defined(SOL2) && defined(INET6) */ + /* * sys_init - System-dependent initialization. */ @@ -150,24 +572,34 @@ void sys_init() { int ifd, x; -#ifndef sun struct ifreq ifr; +#if defined(INET6) && defined(SOL2) + int i6fd; + struct lifreq lifr; +#endif /* defined(INET6) && defined(SOL2) */ +#if !defined(SOL2) struct { union DL_primitives prim; char space[64]; } reply; -#endif +#endif /* !defined(SOL2) */ - ipfd = open("/dev/ip", O_RDWR, 0); + ipfd = open(mux_dev_name, O_RDWR, 0); if (ipfd < 0) fatal("Couldn't open IP device: %m"); +#if defined(INET6) && defined(SOL2) + ip6fd = open(UDP6_DEV_NAME, O_RDWR, 0); + if (ip6fd < 0) + fatal("Couldn't open IP device (2): %m"); +#endif /* defined(INET6) && defined(SOL2) */ + if (default_device && !notty) tty_sid = getsid((pid_t)0); - pppfd = open("/dev/ppp", O_RDWR | O_NONBLOCK, 0); + pppfd = open(PPP_DEV_NAME, O_RDWR | O_NONBLOCK, 0); if (pppfd < 0) - fatal("Can't open /dev/ppp: %m"); + fatal("Can't open %s: %m", PPP_DEV_NAME); if (kdebugflag & 1) { x = PPPDBG_LOG + PPPDBG_DRIVER; strioctl(pppfd, PPPIO_DEBUG, &x, sizeof(int), 0); @@ -177,54 +609,156 @@ sys_init() if (strioctl(pppfd, PPPIO_NEWPPA, &ifunit, 0, sizeof(int)) < 0) fatal("Can't create new PPP interface: %m"); +#if defined(SOL2) + /* + * Since sys_init() is called prior to ifname being set in main(), + * we need to get the ifname now, otherwise slifname(), and others, + * will fail, or maybe, I should move them to a later point ? + * + */ + sprintf(ifname, "ppp%d", ifunit); +#endif /* defined(SOL2) */ /* * Open the ppp device again and link it under the ip multiplexor. * IP will assign a unit number which hopefully is the same as ifunit. * I don't know any way to be certain they will be the same. :-( */ - ifd = open("/dev/ppp", O_RDWR, 0); + ifd = open(PPP_DEV_NAME, O_RDWR, 0); if (ifd < 0) - fatal("Can't open /dev/ppp (2): %m"); + fatal("Can't open %s (2): %m", PPP_DEV_NAME); if (kdebugflag & 1) { x = PPPDBG_LOG + PPPDBG_DRIVER; strioctl(ifd, PPPIO_DEBUG, &x, sizeof(int), 0); } -#ifdef sun - if (ioctl(ifd, I_PUSH, "ip") < 0) { + +#if defined(INET6) && defined(SOL2) + i6fd = open(PPP_DEV_NAME, O_RDWR, 0); + if (i6fd < 0) { + close(ifd); + fatal("Can't open %s (3): %m", PPP_DEV_NAME); + } + if (kdebugflag & 1) { + x = PPPDBG_LOG + PPPDBG_DRIVER; + strioctl(i6fd, PPPIO_DEBUG, &x, sizeof(int), 0); + } +#endif /* defined(INET6) && defined(SOL2) */ + +#if defined(SOL2) + if (ioctl(ifd, I_PUSH, IP_MOD_NAME) < 0) { close(ifd); +#if defined(INET6) + close(i6fd); +#endif /* defined(INET6) */ fatal("Can't push IP module: %m"); } -#ifdef SOL2 /* * Assign ppa according to the unit number returned by ppp device * after plumbing is completed above. */ if (sifppa(ifd, ifunit) < 0) { close (ifd); +#if defined(INET6) + close(i6fd); +#endif /* defined(INET6) */ fatal("Can't set ppa for unit %d: %m", ifunit); } -#endif /* SOL2 */ -#else +#if defined(INET6) + /* + * An IPv6 interface is created anyway, even when the user does not + * explicitly enable it. Note that the interface will be marked + * IPv6 during slifname(). + */ + if (ioctl(i6fd, I_PUSH, IP_MOD_NAME) < 0) { + close(ifd); + close(i6fd); + fatal("Can't push IP module (2): %m"); + } + + /* + * Assign ppa according to the unit number returned by ppp device + * after plumbing is completed above. In addition, mark the interface + * as an IPv6 interface. + */ + if (slifname(i6fd, ifunit) < 0) { + close(ifd); + close(i6fd); + fatal("Can't set ifname for unit %d: %m", ifunit); + } +#endif /* defined(INET6) */ + + ipmuxid = ioctl(ipfd, I_PLINK, ifd); + close(ifd); + if (ipmuxid < 0) { +#if defined(INET6) + close(i6fd); +#endif /* defined(INET6) */ + fatal("Can't I_PLINK PPP device to IP: %m"); + } + + memset(&ifr, 0, sizeof(ifr)); + sprintf(ifr.ifr_name, "%s", ifname); + ifr.ifr_ip_muxid = ipmuxid; + + /* + * In Sol 8 and later, STREAMS dynamic module plumbing feature exists. + * This is so that an arbitrary module can be inserted, or deleted, + * between ip module and the device driver without tearing down the + * existing stream. Such feature requires the mux ids, which is set + * by SIOCSIFMUXID (or SIOCLSIFMUXID). + */ + if (ioctl(ipfd, SIOCSIFMUXID, &ifr) < 0) { + ioctl(ipfd, I_PUNLINK, ipmuxid); +#if defined(INET6) + close(i6fd); +#endif /* defined(INET6) */ + fatal("SIOCSIFMUXID: %m"); + } + +#else /* else if !defined(SOL2) */ + if (dlpi_attach(ifd, ifunit) < 0 || dlpi_get_reply(ifd, &reply.prim, DL_OK_ACK, sizeof(reply)) < 0) { close(ifd); fatal("Can't attach to ppp%d: %m", ifunit); } -#endif + ipmuxid = ioctl(ipfd, I_LINK, ifd); close(ifd); if (ipmuxid < 0) fatal("Can't link PPP device to IP: %m"); +#endif /* defined(SOL2) */ -#ifndef sun +#if defined(INET6) && defined(SOL2) + ip6muxid = ioctl(ip6fd, I_PLINK, i6fd); + close(i6fd); + if (ip6muxid < 0) { + ioctl(ipfd, I_PUNLINK, ipmuxid); + fatal("Can't I_PLINK PPP device to IP (2): %m"); + } + + memset(&lifr, 0, sizeof(lifr)); + sprintf(lifr.lifr_name, "%s", ifname); + lifr.lifr_ip_muxid = ip6muxid; + + /* + * Let IP know of the mux id [see comment for SIOCSIFMUXID above] + */ + if (ioctl(ip6fd, SIOCSLIFMUXID, &lifr) < 0) { + ioctl(ipfd, I_PUNLINK, ipmuxid); + ioctl(ip6fd, I_PUNLINK, ip6muxid); + fatal("Can't link PPP device to IP (2): %m"); + } +#endif /* defined(INET6) && defined(SOL2) */ + +#if !defined(SOL2) /* Set the interface name for the link. */ slprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "ppp%d", ifunit); ifr.ifr_metric = ipmuxid; if (strioctl(ipfd, SIOCSIFNAME, (char *)&ifr, sizeof ifr, 0) < 0) fatal("Can't set interface name %s: %m", ifr.ifr_name); -#endif +#endif /* !defined(SOL2) */ n_pollfds = 0; } @@ -237,14 +771,70 @@ sys_init() void sys_cleanup() { +#if defined(SOL2) struct ifreq ifr; - +#if defined(INET6) + struct lifreq lifr; +#endif /* defined(INET6) */ +#endif /* defined(SOL2) */ + +#if defined(SOL2) && defined(INET6) + if (if6_is_up) + sif6down(0); +#endif /* defined(SOL2) && defined(INET6) */ if (if_is_up) sifdown(0); if (default_route_gateway) cifdefaultroute(0, default_route_gateway, default_route_gateway); if (proxy_arp_addr) cifproxyarp(0, proxy_arp_addr); +#if defined(SOL2) + /* + * Make sure we ask ip what the muxid, because 'ifconfig modlist' will + * unlink and re-link the modules, causing the muxid to change. + */ + memset(&ifr, 0, sizeof(ifr)); + sprintf(ifr.ifr_name, "%s", ifname); + if (ioctl(ipfd, SIOCGIFFLAGS, &ifr) < 0) { + error("SIOCGIFFLAGS: %m"); + return; + } + + if (ioctl(ipfd, SIOCGIFMUXID, &ifr) < 0) { + error("SIOCGIFMUXID: %m"); + return; + } + + ipmuxid = ifr.ifr_ip_muxid; + + if (ioctl(ipfd, I_PUNLINK, ipmuxid) < 0) { + error("Can't I_PUNLINK PPP from IP: %m"); + return; + } +#if defined(INET6) + /* + * Make sure we ask ip what the muxid, because 'ifconfig modlist' will + * unlink and re-link the modules, causing the muxid to change. + */ + memset(&lifr, 0, sizeof(lifr)); + sprintf(lifr.lifr_name, "%s", ifname); + if (ioctl(ip6fd, SIOCGLIFFLAGS, &lifr) < 0) { + error("SIOCGLIFFLAGS: %m"); + return; + } + + if (ioctl(ip6fd, SIOCGLIFMUXID, &lifr) < 0) { + error("SIOCGLIFMUXID: %m"); + return; + } + + ip6muxid = lifr.lifr_ip_muxid; + + if (ioctl(ip6fd, I_PUNLINK, ip6muxid) < 0) { + error("Can't I_PUNLINK PPP from IP (2): %m"); + } +#endif /* defined(INET6) */ +#endif /* defined(SOL2) */ } /* @@ -254,6 +844,9 @@ void sys_close() { close(ipfd); +#if defined(INET6) && defined(SOL2) + close(ip6fd); +#endif /* defined(INET6) && defined(SOL2) */ if (pppfd >= 0) close(pppfd); } @@ -301,7 +894,28 @@ ppp_available() { struct stat buf; - return stat("/dev/ppp", &buf) >= 0; + return stat(PPP_DEV_NAME, &buf) >= 0; +} + +/* + * any_compressions - see if compression is enabled or not + * + * In the STREAMS implementation of kernel-portion pppd, + * the comp STREAMS module performs the ACFC, PFC, as well + * CCP and VJ compressions. However, if the user has explicitly + * declare to not enable them from the command line, there is + * no point of having the comp module be pushed on the stream. + */ +static int +any_compressions() +{ + if ((!lcp_wantoptions[0].neg_accompression) && + (!lcp_wantoptions[0].neg_pcompression) && + (!ccp_protent.enabled_flag) && + (!ipcp_wantoptions[0].neg_vj)) { + return 0; + } + return 1; } /* @@ -325,7 +939,7 @@ establish_ppp(fd) tty_npushed = 0; if(!sync_serial) { - if (ioctl(fd, I_PUSH, "ppp_ahdl") < 0) { + if (ioctl(fd, I_PUSH, AHDLC_MOD_NAME) < 0) { error("Couldn't push PPP Async HDLC module: %m"); return -1; } @@ -335,12 +949,21 @@ establish_ppp(fd) i = PPPDBG_LOG + PPPDBG_AHDLC; strioctl(pppfd, PPPIO_DEBUG, &i, sizeof(int), 0); } - if (ioctl(fd, I_PUSH, "ppp_comp") < 0) - error("Couldn't push PPP compression module: %m"); - else - ++tty_npushed; + /* + * There's no need to push comp module if we don't intend + * to compress anything + */ + if (any_compressions()) { + if (ioctl(fd, I_PUSH, COMP_MOD_NAME) < 0) + error("Couldn't push PPP compression module: %m"); + else + ++tty_npushed; + } + if (kdebugflag & 2) { - i = PPPDBG_LOG + PPPDBG_COMP; + i = PPPDBG_LOG; + if (any_compressions()) + i += PPPDBG_COMP; strioctl(pppfd, PPPIO_DEBUG, &i, sizeof(int), 0); } @@ -507,6 +1130,18 @@ struct speed { #endif #ifdef B115200 { 115200, B115200 }, +#endif +#ifdef B153600 + { 153600, B153600 }, +#endif +#ifdef B230400 + { 230400, B230400 }, +#endif +#ifdef B307200 + { 307200, B307200 }, +#endif +#ifdef B460800 + { 460800, B460800 }, #endif { 0, 0 } }; @@ -566,7 +1201,7 @@ set_up_tty(fd, local) #ifndef CRTSCTS termiox_ok = 1; - if (ioctl (fd, TCGETX, &tiox) < 0) { + if (!sync_serial && ioctl (fd, TCGETX, &tiox) < 0) { termiox_ok = 0; if (errno != ENOTTY) error("TCGETX: %m"); @@ -578,7 +1213,8 @@ set_up_tty(fd, local) #ifndef CRTSCTS inittermiox = tiox; #endif - ioctl(fd, TIOCGWINSZ, &wsinfo); + if (!sync_serial) + ioctl(fd, TIOCGWINSZ, &wsinfo); } tios.c_cflag &= ~(CSIZE | CSTOPB | PARENB | CLOCAL); @@ -622,7 +1258,7 @@ set_up_tty(fd, local) * We can't proceed if the serial port speed is 0, * since that implies that the serial port is disabled. */ - if (speed == B0) + if ((speed == B0) && !sync_serial) fatal("Baud rate for %s is 0; need explicit baud rate", devnam); } @@ -630,13 +1266,14 @@ set_up_tty(fd, local) fatal("tcsetattr: %m"); #ifndef CRTSCTS - if (termiox_ok && ioctl (fd, TCSETXF, &tiox) < 0){ + if (!sync_serial && termiox_ok && ioctl (fd, TCSETXF, &tiox) < 0){ error("TCSETXF: %m"); } #endif baud_rate = inspeed = baud_rate_of(speed); - restore_term = 1; + if (!sync_serial) + restore_term = 1; } /* @@ -656,16 +1293,17 @@ restore_tty(fd) */ inittermios.c_lflag &= ~(ECHO | ECHONL); } - if (tcsetattr(fd, TCSAFLUSH, &inittermios) < 0) + if (!sync_serial && tcsetattr(fd, TCSAFLUSH, &inittermios) < 0) if (!hungup && errno != ENXIO) warn("tcsetattr: %m"); #ifndef CRTSCTS - if (ioctl (fd, TCSETXF, &inittermiox) < 0){ + if (!sync_serial && ioctl (fd, TCSETXF, &inittermiox) < 0){ if (!hungup && errno != ENXIO) error("TCSETXF: %m"); } #endif - ioctl(fd, TIOCSWINSZ, &wsinfo); + if (!sync_serial) + ioctl(fd, TIOCSWINSZ, &wsinfo); restore_term = 0; } } @@ -707,8 +1345,8 @@ output(unit, p, len) int retries; struct pollfd pfd; - if (debug) - dbglog("sent %P", p, len); + dump_packet("sent", p, len); + if (snoop_send_hook) snoop_send_hook(p, len); data.len = len; data.buf = (caddr_t) p; @@ -877,6 +1515,10 @@ ppp_send_config(unit, mtu, asyncmap, pcomp, accomp) { int cf[2]; struct ifreq ifr; +#if defined(INET6) && defined(SOL2) + struct lifreq lifr; + int fd; +#endif /* defined(INET6) && defined(SOL2) */ link_mtu = mtu; if (strioctl(pppfd, PPPIO_MTU, &mtu, sizeof(mtu), 0) < 0) { @@ -885,13 +1527,15 @@ ppp_send_config(unit, mtu, asyncmap, pcomp, accomp) error("Couldn't set MTU: %m"); } if (fdmuxid >= 0) { - /* can't set these if we don't have a stream attached below /dev/ppp */ - if (strioctl(pppfd, PPPIO_XACCM, &asyncmap, sizeof(asyncmap), 0) < 0) { - error("Couldn't set transmit ACCM: %m"); + if (!sync_serial) { + if (strioctl(pppfd, PPPIO_XACCM, &asyncmap, sizeof(asyncmap), 0) < 0) { + error("Couldn't set transmit ACCM: %m"); + } } cf[0] = (pcomp? COMP_PROT: 0) + (accomp? COMP_AC: 0); cf[1] = COMP_PROT | COMP_AC; - if (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) { + if (any_compressions() && + strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) { error("Couldn't set prot/AC compression: %m"); } } @@ -903,6 +1547,21 @@ ppp_send_config(unit, mtu, asyncmap, pcomp, accomp) if (ioctl(ipfd, SIOCSIFMTU, &ifr) < 0) { error("Couldn't set IP MTU: %m"); } + +#if defined(INET6) && defined(SOL2) + fd = socket(AF_INET6, SOCK_DGRAM, 0); + if (fd < 0) + error("Couldn't open IPv6 socket: %m"); + + memset(&lifr, 0, sizeof(lifr)); + strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name)); + lifr.lifr_mtu = link_mtu; + if (ioctl(fd, SIOCSLIFMTU, &lifr) < 0) { + close(fd); + error("Couldn't set IPv6 MTU: %m"); + } + close(fd); +#endif /* defined(INET6) && defined(SOL2) */ } /* @@ -913,6 +1572,9 @@ ppp_set_xaccm(unit, accm) int unit; ext_accm accm; { + if (sync_serial) + return; + if (fdmuxid >= 0 && strioctl(pppfd, PPPIO_XACCM, accm, sizeof(ext_accm), 0) < 0) { if (!hungup || errno != ENXIO) @@ -939,13 +1601,15 @@ ppp_recv_config(unit, mru, asyncmap, pcomp, accomp) error("Couldn't set MRU: %m"); } if (fdmuxid >= 0) { - /* can't set these if we don't have a stream attached below /dev/ppp */ - if (strioctl(pppfd, PPPIO_RACCM, &asyncmap, sizeof(asyncmap), 0) < 0) { - error("Couldn't set receive ACCM: %m"); + if (!sync_serial) { + if (strioctl(pppfd, PPPIO_RACCM, &asyncmap, sizeof(asyncmap), 0) < 0) { + error("Couldn't set receive ACCM: %m"); + } } cf[0] = (pcomp? DECOMP_PROT: 0) + (accomp? DECOMP_AC: 0); cf[1] = DECOMP_PROT | DECOMP_AC; - if (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) { + if (any_compressions() && + strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) { error("Couldn't set prot/AC decompression: %m"); } } @@ -1004,7 +1668,8 @@ get_ppp_stats(u, stats) { struct ppp_stats s; - if (strioctl(pppfd, PPPIO_GETSTAT, &s, 0, sizeof(s)) < 0) { + if (!sync_serial && + strioctl(pppfd, PPPIO_GETSTAT, &s, 0, sizeof(s)) < 0) { error("Couldn't get link statistics: %m"); return 0; } @@ -1158,6 +1823,139 @@ sifnpmode(u, proto, mode) return 1; } +#if defined(SOL2) && defined(INET6) +/* + * sif6up - Config the IPv6 interface up and enable IPv6 packets to pass. + */ +int +sif6up(u) + int u; +{ + struct lifreq lifr; + int fd; + + fd = socket(AF_INET6, SOCK_DGRAM, 0); + if (fd < 0) { + return 0; + } + + memset(&lifr, 0, sizeof(lifr)); + strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name)); + if (ioctl(fd, SIOCGLIFFLAGS, &lifr) < 0) { + close(fd); + return 0; + } + + lifr.lifr_flags |= IFF_UP; + strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name)); + if (ioctl(fd, SIOCSLIFFLAGS, &lifr) < 0) { + close(fd); + return 0; + } + + if6_is_up = 1; + close(fd); + return 1; +} + +/* + * sifdown - Config the IPv6 interface down and disable IPv6. + */ +int +sif6down(u) + int u; +{ + struct lifreq lifr; + int fd; + + fd = socket(AF_INET6, SOCK_DGRAM, 0); + if (fd < 0) + return 0; + + memset(&lifr, 0, sizeof(lifr)); + strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name)); + if (ioctl(fd, SIOCGLIFFLAGS, &lifr) < 0) { + close(fd); + return 0; + } + + lifr.lifr_flags &= ~IFF_UP; + strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name)); + if (ioctl(fd, SIOCGLIFFLAGS, &lifr) < 0) { + close(fd); + return 0; + } + + if6_is_up = 0; + close(fd); + return 1; +} + +/* + * sif6addr - Config the interface with an IPv6 link-local address + */ +int +sif6addr(u, o, h) + int u; + eui64_t o, h; +{ + struct lifreq lifr; + struct sockaddr_storage laddr; + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&laddr; + int fd; + + fd = socket(AF_INET6, SOCK_DGRAM, 0); + if (fd < 0) + return 0; + + memset(&lifr, 0, sizeof(lifr)); + strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name)); + + /* + * Do this because /dev/ppp responds to DL_PHYS_ADDR_REQ with + * zero values, hence the interface token came to be zero too, + * and without this, in.ndpd will complain + */ + IN6_LLTOKEN_FROM_EUI64(lifr, sin6, o); + if (ioctl(fd, SIOCSLIFTOKEN, &lifr) < 0) { + close(fd); + return 0; + } + + /* + * Set the interface address and destination address + */ + IN6_LLADDR_FROM_EUI64(lifr, sin6, o); + if (ioctl(fd, SIOCSLIFADDR, &lifr) < 0) { + close(fd); + return 0; + } + + memset(&lifr, 0, sizeof(lifr)); + strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name)); + IN6_LLADDR_FROM_EUI64(lifr, sin6, h); + if (ioctl(fd, SIOCSLIFDSTADDR, &lifr) < 0) { + close(fd); + return 0; + } + + return 1; +} + +/* + * cif6addr - Remove the IPv6 address from interface + */ +int +cif6addr(u, o, h) + int u; + eui64_t o, h; +{ + return 1; +} + +#endif /* defined(SOL2) && defined(INET6) */ + + #define INET_ADDR(x) (((struct sockaddr_in *) &(x))->sin_addr.s_addr) /* @@ -1420,33 +2218,13 @@ get_ether_addr(ipaddr, hwaddr) } /* - * get_hw_addr - obtain the hardware address for a named interface. + * get_hw_addr_dlpi - obtain the hardware address using DLPI */ static int -get_hw_addr(name, ina, hwaddr) +get_hw_addr_dlpi(name, hwaddr) char *name; - u_int32_t ina; struct sockaddr *hwaddr; { -#if 1 - /* New way - get the address by doing an arp request. */ - int s; - struct arpreq req; - - s = socket(AF_INET, SOCK_DGRAM, 0); - if (s < 0) - return 0; - memset(&req, 0, sizeof(req)); - req.arp_pa.sa_family = AF_INET; - INET_ADDR(req.arp_pa) = ina; - if (ioctl(s, SIOCGARP, &req) < 0) { - error("Couldn't get ARP entry for %s: %m", ip_ntoa(ina)); - return 0; - } - *hwaddr = req.arp_ha; - hwaddr->sa_family = AF_UNSPEC; - -#else /* 0 */ char *p, *q; int unit, iffd, adrlen; unsigned char *adrp; @@ -1485,15 +2263,44 @@ get_hw_addr(name, ina, hwaddr) adrlen = reply.prim.info_ack.dl_addr_length; adrp = (unsigned char *)&reply + reply.prim.info_ack.dl_addr_offset; + #if DL_CURRENT_VERSION >= 2 if (reply.prim.info_ack.dl_sap_length < 0) adrlen += reply.prim.info_ack.dl_sap_length; else adrp += reply.prim.info_ack.dl_sap_length; #endif + hwaddr->sa_family = AF_UNSPEC; memcpy(hwaddr->sa_data, adrp, adrlen); -#endif /* 0 */ + + return 1; +} +/* + * get_hw_addr - obtain the hardware address for a named interface. + */ +static int +get_hw_addr(name, ina, hwaddr) + char *name; + u_int32_t ina; + struct sockaddr *hwaddr; +{ + /* New way - get the address by doing an arp request. */ + int s; + struct arpreq req; + + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) + return 0; + memset(&req, 0, sizeof(req)); + req.arp_pa.sa_family = AF_INET; + INET_ADDR(req.arp_pa) = ina; + if (ioctl(s, SIOCGARP, &req) < 0) { + error("Couldn't get ARP entry for %s: %m", ip_ntoa(ina)); + return 0; + } + *hwaddr = req.arp_ha; + hwaddr->sa_family = AF_UNSPEC; return 1; } @@ -1844,9 +2651,9 @@ have_route_to(addr) mib2_ipRouteEntry_t routes[8]; mib2_ipRouteEntry_t *rp; - fd = open("/dev/ip", O_RDWR); + fd = open(mux_dev_name, O_RDWR); if (fd < 0) { - warn("have_route_to: couldn't open /dev/ip: %m"); + warn("have_route_to: couldn't open %s: %m", mux_dev_name); return -1; }