/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* FIXME: from snoop. Use common library when it comes into existence */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DLMAXWAIT (10) /* max wait in seconds for response */ #define DLMAXBUF (80) typedef union dlbuf { union DL_primitives dl; char *buf[DLMAXBUF]; } dlbuf_t; static int timed_getmsg(int, struct strbuf *, struct strbuf *, int *, int); static boolean_t expecting(ulong_t, union DL_primitives *); /* * Issue DL_INFO_REQ and wait for DL_INFO_ACK. */ static int dlinforeq(int fd, dl_info_ack_t *infoackp) { dlbuf_t buf; struct strbuf ctl; int flags; buf.dl.info_req.dl_primitive = DL_INFO_REQ; ctl.maxlen = sizeof (buf); ctl.len = DL_INFO_REQ_SIZE; ctl.buf = (char *)&buf.dl; flags = RS_HIPRI; if (putmsg(fd, &ctl, NULL, flags) < 0) return (-1); if (timed_getmsg(fd, &ctl, NULL, &flags, DLMAXWAIT) != 0) return (-1); if (!expecting(DL_INFO_ACK, &buf.dl)) return (-1); if (ctl.len < DL_INFO_ACK_SIZE) return (-1); if (flags != RS_HIPRI) return (-1); if (infoackp != NULL) *infoackp = buf.dl.info_ack; return (0); } /* * Issue DL_ATTACH_REQ. * Return zero on success, nonzero on error. */ static int dlattachreq(int fd, ulong_t ppa) { dlbuf_t buf; struct strbuf ctl; int flags; buf.dl.attach_req.dl_primitive = DL_ATTACH_REQ; buf.dl.attach_req.dl_ppa = ppa; ctl.maxlen = sizeof (buf.dl); ctl.len = DL_ATTACH_REQ_SIZE; ctl.buf = (char *)&buf.dl; flags = 0; if (putmsg(fd, &ctl, NULL, flags) < 0) return (-1); if (timed_getmsg(fd, &ctl, NULL, &flags, DLMAXWAIT) != 0) return (-1); if (!expecting(DL_OK_ACK, &buf.dl)) return (-1); return (0); } static int timed_getmsg(int fd, struct strbuf *ctlp, struct strbuf *datap, int *flagsp, int timeout) { struct pollfd pfd; int rc; pfd.fd = fd; pfd.events = POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI; if ((rc = poll(&pfd, 1, timeout * 1000)) == 0) return (0); else if (rc == -1) return (0); /* poll returned > 0 for this fd so getmsg should not block */ *flagsp = 0; if ((rc = getmsg(fd, ctlp, datap, flagsp)) < 0) return (0); /* * Check for MOREDATA and/or MORECTL. */ if ((rc & (MORECTL | MOREDATA)) == (MORECTL | MOREDATA)) return (-1); if (rc & MORECTL) return (-1); if (rc & MOREDATA) return (-1); /* * Check for at least sizeof (long) control data portion. */ if (ctlp->len < sizeof (long)) return (-1); return (0); } static boolean_t expecting(ulong_t prim, union DL_primitives *dlp) { if (dlp->dl_primitive == DL_ERROR_ACK || dlp->dl_primitive != prim) return (B_FALSE); return (B_TRUE); } /* * Convert a device id to a ppa value * e.g. "le0" -> 0 */ static int device_ppa(char *device) { char *p; char *tp; p = strpbrk(device, "0123456789"); if (p == NULL) return (0); /* ignore numbers within device names */ for (tp = p; *tp != '\0'; tp++) if (!isdigit(*tp)) return (device_ppa(tp)); return (atoi(p)); } /* * Convert a device id to a pathname. * DLPI style 1 devices: "le0" -> "/dev/le0". * DLPI style 2 devices: "le0" -> "/dev/le". */ static char * device_path(char *device) { static char buff[IF_NAMESIZE + 1]; struct stat st; char *p; (void) strcpy(buff, "/dev/"); (void) strlcat(buff, device, IF_NAMESIZE); if (stat(buff, &st) == 0) return (buff); for (p = buff + (strlen(buff) - 1); p > buff; p--) { if (isdigit(*p)) *p = '\0'; else break; } return (buff); } /* * Open up the device, and attach if needed. */ int ifname_open(char *device) { char *devname; ulong_t ppa; int netfd; dl_info_ack_t netdl; /* * Determine which network device * to use if none given. * Should get back a value like "/dev/le0". */ devname = device_path(device); if ((netfd = open(devname, O_RDWR)) < 0) return (-1); ppa = device_ppa(device); /* * Check for DLPI Version 2. */ if (dlinforeq(netfd, &netdl) != 0) { (void) close(netfd); return (-1); } if (netdl.dl_version != DL_VERSION_2) { (void) close(netfd); return (-1); } /* * Attach for DLPI Style 2. */ if (netdl.dl_provider_style == DL_STYLE2) { if (dlattachreq(netfd, ppa) != 0) { (void) close(netfd); return (-1); } /* Reread more specific information */ if (dlinforeq(netfd, &netdl) != 0) { (void) close(netfd); return (-1); } } return (netfd); }