/* * $Id$ * Copyright 2006-2012 Chung, Hyung-Hwan. This file is part of QSE. QSE is free software: you can redistribute it and/or modify it uheadr the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. QSE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with QSE. If not, see . */ #include #include #include #include "mem.h" #if defined(_WIN32) # include # include # include #elif defined(__OS2__) # include # include # include # include # include # if defined(TCPV40HDRS) # include # else # include # endif #elif defined(__DOS__) /* TODO: */ #else # include "syscall.h" # include # include # if defined(HAVE_SYS_IOCTL_H) # include # endif # if defined(HAVE_NET_IF_H) # include # endif # if defined(HAVE_NET_IF_DL_H) # include # endif # if defined(HAVE_SYS_SOCKIO_H) # include # endif # if defined(HAVE_IFADDRS_H) # include # endif # if defined(HAVE_SYS_SYSCTL_H) # include # endif # if defined(HAVE_SYS_STROPTS_H) # include /* stream options */ # endif # if defined(HAVE_SYS_MACSTAT_H) # include # endif # if defined(HAVE_LINUX_ETHTOOL_H) # include # endif # if defined(HAVE_LINUX_SOCKIOS_H) # include # endif #endif /* #if defined(HAVE_NET_IF_DL_H) # include #endif #ifndef SIOCGLIFHWADDR #define SIOCGLIFHWADDR _IOWR('i', 192, struct lifreq) #endif struct lifreq lif; memset(&lif, 0, sizeof(lif)); strlcpy(lif.lifr_name, ifname, sizeof(lif.lifr_name)); if (ioctl(sock, SIOCGLIFHWADDR, &lif) != -1) { struct sockaddr_dl *sp; sp = (struct sockaddr_dl *)&lif.lifr_addr; memcpy(buf, &sp->sdl_data[0], sp->sdl_alen); return sp->sdl_alen; } */ #if 0 #if defined(SIOCGLIFCONF) && defined(SIOCGLIFNUM) && \ defined(HAVE_STRUCT_LIFCONF) && defined(HAVE_STRUCT_LIFREQ) static int get_nwifs (qse_mmgr_t* mmgr, int s, int f, qse_xptl_t* nwifs) { struct lifnum ifn; struct lifconf ifc; qse_xptl_t b; qse_size_t ifcount; ifcount = 0; b.ptr = QSE_NULL; b.len = 0; do { ifn.lifn_family = f; ifn.lifn_flags = 0; if (ioctl (s, SIOCGLIFNUM, &ifn) <= -1) goto oops; if (b.ptr) { /* b.ptr won't be QSE_NULL when retrying */ if (ifn.lifn_count <= ifcount) break; } /* +1 for extra space to leave empty * if SIOCGLIFCONF returns the same number of * intefaces as SIOCLIFNUM */ b.len = (ifn.lifn_count + 1) * QSE_SIZEOF(struct lifreq); b.ptr = QSE_MMGR_ALLOC (mmgr, b.len); if (b.ptr == QSE_NULL) goto oops; ifc.lifc_family = f; ifc.lifc_flags = 0; ifc.lifc_len = b.len; ifc.lifc_buf = b.ptr; if (ioctl (s, SIOCGLIFCONF, &ifc) <= -1) goto oops; ifcount = ifc.lifc_len / QSE_SIZEOF(struct lifreq); } while (ifcount > ifn.lifn_count); /* the while condition above is for checking if * the buffer got full. when it's full, there is a chance * that there are more interfaces. */ nwifs->ptr = b.ptr; nwifs->len = ifcount; return 0; oops: if (b.ptr) QSE_MMGR_FREE (mmgr, b.ptr); return -1; } #endif #endif #if 0 static void free_nwifcfg (qse_mmgr_t* mmgr, qse_nwifcfg_node_t* cfg) { qse_nwifcfg_node_t* cur; while (cfg) { cur = cfg; cfg = cur->next; if (cur->name) QSE_MMGR_FREE (mmgr, cur->name); QSE_MMGR_FREE (mmgr, cur); } } int qse_getnwifcfg (qse_nwifcfg_t* cfg) { #if defined(SIOCGLIFCONF) struct lifreq* ifr, ifrbuf; qse_size_t i; int s, f; int s4 = -1; int s6 = -1; qse_xptl_t nwifs = { QSE_NULL, 0 }; qse_nwifcfg_node_t* head = QSE_NULL; qse_nwifcfg_node_t* tmp; QSE_ASSERT (cfg->mmgr != QSE_NULL); #if defined(AF_INET) || defined(AF_INET6) #if defined(AF_INET) s4 = socket (AF_INET, SOCK_DGRAM, 0); if (s4 <= -1) goto oops; #endif #if defined(AF_INET6) s6 = socket (AF_INET6, SOCK_DGRAM, 0); if (s6 <= -1) goto oops; #endif #else /* no implementation */ goto oops; #endif if (get_nwifs (cfg->mmgr, s4, AF_UNSPEC, &nwifs) <= -1) goto oops; ifr = nwifs.ptr; for (i = 0; i < nwifs.len; i++, ifr++) { f = ifr->lifr_addr.ss_family; if (f == AF_INET) s = s4; else if (f == AF_INET6) s = s6; else continue; tmp = QSE_MMGR_ALLOC (cfg->mmgr, QSE_SIZEOF(*tmp)); if (tmp == QSE_NULL) goto oops; QSE_MEMSET (tmp, 0, QSE_SIZEOF(*tmp)); tmp->next = head; head = tmp; #if defined(QSE_CHAR_IS_MCHAR) head->name = qse_mbsdup (ifr->lifr_name, cfg->mmgr); #else head->name = qse_mbstowcsdup (ifr->lifr_name, QSE_NULL, cfg->mmgr); #endif if (head->name == QSE_NULL) goto oops; qse_skadtonwad (&ifr->lifr_addr, &head->addr); qse_mbsxcpy (ifrbuf.lifr_name, QSE_SIZEOF(ifrbuf.lifr_name), ifr->lifr_name); if (ioctl (s, SIOCGLIFFLAGS, &ifrbuf) <= -1) goto oops; if (ifrbuf.lifr_flags & IFF_UP) head->flags |= QSE_NWIFCFG_UP; if (ifrbuf.lifr_flags & IFF_BROADCAST) { if (ioctl (s, SIOCGLIFBRDADDR, &ifrbuf) <= -1) goto oops; qse_skadtonwad (&ifrbuf.lifr_addr, &head->bcast); head->flags |= QSE_NWIFCFG_BCAST; } if (ifrbuf.lifr_flags & IFF_POINTOPOINT) { if (ioctl (s, SIOCGLIFDSTADDR, &ifrbuf) <= -1) goto oops; qse_skadtonwad (&ifrbuf.lifr_addr, &head->ptop); head->flags |= QSE_NWIFCFG_PTOP; } if (ioctl (s, SIOCGLIFINDEX, &ifrbuf) <= -1) goto oops; head->index = ifrbuf.lifr_index; if (ioctl (s, SIOCGLIFNETMASK, &ifrbuf) <= -1) goto oops; qse_skadtonwad (&ifrbuf.lifr_addr, &head->mask); } QSE_MMGR_FREE (cfg->mmgr, nwifs.ptr); close (s6); close (s4); cfg->list = head; return 0; oops: if (head) free_nwifcfg (cfg->mmgr, head); if (nwifs.ptr) QSE_MMGR_FREE (cfg->mmgr, nwifs.ptr); if (s6 >= 0) close (s6); if (s4 >= 0) close (s4); return -1; #else /* TODO */ return QSE_NULL; #endif } void qse_freenwifcfg (qse_nwifcfg_t* cfg) { free_nwifcfg (cfg->mmgr, cfg->list); cfg->list = QSE_NULL; } #endif #if defined(__linux) static void read_proc_net_if_inet6 (qse_nwifcfg_t* cfg, struct ifreq* ifr) { #if 0 qse_sio_t* sio; qse_mchar_t line[128]; /* TODO */ sio = qse_sio_open (QSE_MMGR_GETDFL(), 0, QSE_T("proc/net/if_inet6"), QSE_SIO_IGNOREMBWCERR | QSE_SIO_READ); if (sio) { qse_ssize_t x; while (1) { x = qse_sio_getmbs (sio, line, QSE_COUNTOF(line)); if (x == -1) break; } qse_sio_close (sio); } #endif } #endif static int get_nwifcfg (int s, qse_nwifcfg_t* cfg, struct ifreq* ifr) { #if defined(_WIN32) return -1; #elif defined(__OS2__) return -1; #elif defined(__DOS__) return -1; #elif defined(SIOCGLIFADDR) && defined(SIOCGLIFINDEX) && \ defined(HAVE_STRUCT_LIFCONF) && defined(HAVE_STRUCT_LIFREQ) /* opensolaris */ struct lifreq lifrbuf; qse_size_t ml, wl; QSE_MEMSET (&lifrbuf, 0, QSE_SIZEOF(lifrbuf)); qse_mbsxcpy (lifrbuf.lifr_name, QSE_SIZEOF(lifrbuf.lifr_name), ifr->ifr_name); if (ioctl (s, SIOCGLIFINDEX, &lifrbuf) <= -1) return -1; cfg->index = lifrbuf.lifr_index; if (ioctl (s, SIOCGLIFFLAGS, &lifrbuf) <= -1) return -1; cfg->flags = 0; if (lifrbuf.lifr_flags & IFF_UP) cfg->flags |= QSE_NWIFCFG_UP; if (lifrbuf.lifr_flags & IFF_RUNNING) cfg->flags |= QSE_NWIFCFG_RUNNING; if (lifrbuf.lifr_flags & IFF_BROADCAST) cfg->flags |= QSE_NWIFCFG_BCAST; if (lifrbuf.lifr_flags & IFF_POINTOPOINT) cfg->flags |= QSE_NWIFCFG_PTOP; if (ioctl (s, SIOCGLIFMTU, &lifrbuf) <= -1) return -1; cfg->mtu = lifrbuf.lifr_mtu; qse_clearnwad (&cfg->addr, QSE_NWAD_NX); qse_clearnwad (&cfg->mask, QSE_NWAD_NX); qse_clearnwad (&cfg->bcast, QSE_NWAD_NX); qse_clearnwad (&cfg->ptop, QSE_NWAD_NX); QSE_MEMSET (cfg->ethw, 0, QSE_SIZEOF(cfg->ethw)); if (ioctl (s, SIOCGLIFADDR, &lifrbuf) >= 0) qse_skadtonwad (&lifrbuf.lifr_addr, &cfg->addr); if (ioctl (s, SIOCGLIFNETMASK, &lifrbuf) >= 0) qse_skadtonwad (&lifrbuf.lifr_addr, &cfg->mask); if ((cfg->flags & QSE_NWIFCFG_BCAST) && ioctl (s, SIOCGLIFBRDADDR, &lifrbuf) >= 0) { qse_skadtonwad (&lifrbuf.lifr_broadaddr, &cfg->bcast); } if ((cfg->flags & QSE_NWIFCFG_PTOP) && ioctl (s, SIOCGLIFDSTADDR, &lifrbuf) >= 0) { qse_skadtonwad (&lifrbuf.lifr_dstaddr, &cfg->ptop); } #if defined(SIOCGENADDR) { if (ioctl (s, SIOCGENADDR, ifr) >= 0 && QSE_SIZEOF(ifr->ifr_enaddr) >= QSE_SIZEOF(cfg->ethw)) { QSE_MEMCPY (cfg->ethw, ifr->ifr_enaddr, QSE_SIZEOF(cfg->ethw)); } /* TODO: try DLPI if SIOCGENADDR fails... */ } #endif return 0; #elif defined(SIOCGLIFADDR) && defined(HAVE_STRUCT_IF_LADDRREQ) && !defined(SIOCGLIFINDEX) /* freebsd */ qse_size_t ml, wl; #if defined(SIOCGIFINDEX) if (ioctl (s, SIOCGIFINDEX, ifr) <= -1) return -1; #if defined(HAVE_STRUCT_IFREQ_IFR_IFINDEX) cfg->index = ifr->ifr_ifindex; #else cfg->index = ifr->ifr_index; #endif #else cfg->index = 0; #endif if (ioctl (s, SIOCGIFFLAGS, ifr) <= -1) return -1; cfg->flags = 0; if (ifr->ifr_flags & IFF_UP) cfg->flags |= QSE_NWIFCFG_UP; if (ifr->ifr_flags & IFF_RUNNING) cfg->flags |= QSE_NWIFCFG_RUNNING; if (ifr->ifr_flags & IFF_BROADCAST) cfg->flags |= QSE_NWIFCFG_BCAST; if (ifr->ifr_flags & IFF_POINTOPOINT) cfg->flags |= QSE_NWIFCFG_PTOP; if (ioctl (s, SIOCGIFMTU, ifr) <= -1) return -1; #if defined(HAVE_STRUCT_IFREQ_IFR_MTU) cfg->mtu = ifr->ifr_mtu; #else /* well, this is a bit dirty. but since all these are unions, * the name must not really matter. some OSes just omitts defining * the MTU field */ cfg->mtu = ifr->ifr_metric; #endif qse_clearnwad (&cfg->addr, QSE_NWAD_NX); qse_clearnwad (&cfg->mask, QSE_NWAD_NX); qse_clearnwad (&cfg->bcast, QSE_NWAD_NX); qse_clearnwad (&cfg->ptop, QSE_NWAD_NX); QSE_MEMSET (cfg->ethw, 0, QSE_SIZEOF(cfg->ethw)); if (cfg->type == QSE_NWIFCFG_IN6) { struct if_laddrreq iflrbuf; QSE_MEMSET (&iflrbuf, 0, QSE_SIZEOF(iflrbuf)); qse_mbsxcpy (iflrbuf.iflr_name, QSE_SIZEOF(iflrbuf.iflr_name), ifr->ifr_name); if (ioctl (s, SIOCGLIFADDR, &iflrbuf) >= 0) { qse_skadtonwad (&iflrbuf.addr, &cfg->addr); cfg->mask.type = QSE_NWAD_IN6; qse_prefixtoip6ad (iflrbuf.prefixlen, &cfg->mask.u.in6.addr); if (cfg->flags & QSE_NWIFCFG_PTOP) qse_skadtonwad (&iflrbuf.dstaddr, &cfg->ptop); } } else { if (ioctl (s, SIOCGIFADDR, ifr) >= 0) qse_skadtonwad (&ifr->ifr_addr, &cfg->addr); if (ioctl (s, SIOCGIFNETMASK, ifr) >= 0) qse_skadtonwad (&ifr->ifr_addr, &cfg->mask); if ((cfg->flags & QSE_NWIFCFG_BCAST) && ioctl (s, SIOCGIFBRDADDR, ifr) >= 0) { qse_skadtonwad (&ifr->ifr_broadaddr, &cfg->bcast); } if ((cfg->flags & QSE_NWIFCFG_PTOP) && ioctl (s, SIOCGIFDSTADDR, ifr) >= 0) { qse_skadtonwad (&ifr->ifr_dstaddr, &cfg->ptop); } } #if defined(CTL_NET) && defined(AF_ROUTE) && defined(AF_LINK) { int mib[6]; size_t len; mib[0] = CTL_NET; mib[1] = AF_ROUTE; mib[2] = 0; mib[3] = AF_LINK; mib[4] = NET_RT_IFLIST; mib[5] = cfg->index; if (sysctl (mib, QSE_COUNTOF(mib), QSE_NULL, &len, QSE_NULL, 0) >= 0) { qse_mmgr_t* mmgr = QSE_MMGR_GETDFL(); void* buf; buf = QSE_MMGR_ALLOC (mmgr, len); if (buf) { if (sysctl (mib, QSE_COUNTOF(mib), buf, &len, QSE_NULL, 0) >= 0) { struct sockaddr_dl* sadl; sadl = ((struct if_msghdr*)buf + 1); /* i don't really care if it's really ethernet * so long as the data is long enough */ if (sadl->sdl_alen >= QSE_COUNTOF(cfg->ethw)) QSE_MEMCPY (cfg->ethw, LLADDR(sadl), QSE_SIZEOF(cfg->ethw)); } QSE_MMGR_FREE (mmgr, buf); } } } #endif return 0; #elif defined(SIOCGIFADDR) #if defined(SIOCGIFINDEX) if (ioctl (s, SIOCGIFINDEX, ifr) <= -1) return -1; #if defined(HAVE_STRUCT_IFREQ_IFR_IFINDEX) cfg->index = ifr->ifr_ifindex; #else cfg->index = ifr->ifr_index; #endif #else cfg->index = 0; #endif if (ioctl (s, SIOCGIFFLAGS, ifr) <= -1) return -1; cfg->flags = 0; if (ifr->ifr_flags & IFF_UP) cfg->flags |= QSE_NWIFCFG_UP; if (ifr->ifr_flags & IFF_RUNNING) cfg->flags |= QSE_NWIFCFG_RUNNING; if (ifr->ifr_flags & IFF_BROADCAST) cfg->flags |= QSE_NWIFCFG_BCAST; if (ifr->ifr_flags & IFF_POINTOPOINT) cfg->flags |= QSE_NWIFCFG_PTOP; if (ioctl (s, SIOCGIFMTU, ifr) <= -1) return -1; #if defined(HAVE_STRUCT_IFREQ_IFR_MTU) cfg->mtu = ifr->ifr_mtu; #else /* well, this is a bit dirty. but since all these are unions, * the name must not really matter. SCO just omits defining * the MTU field, and uses ifr_metric instead */ cfg->mtu = ifr->ifr_metric; #endif qse_clearnwad (&cfg->addr, QSE_NWAD_NX); qse_clearnwad (&cfg->mask, QSE_NWAD_NX); qse_clearnwad (&cfg->bcast, QSE_NWAD_NX); qse_clearnwad (&cfg->ptop, QSE_NWAD_NX); QSE_MEMSET (cfg->ethw, 0, QSE_SIZEOF(cfg->ethw)); if (ioctl (s, SIOCGIFADDR, ifr) >= 0) qse_skadtonwad (&ifr->ifr_addr, &cfg->addr); if (ioctl (s, SIOCGIFNETMASK, ifr) >= 0) qse_skadtonwad (&ifr->ifr_addr, &cfg->mask); #if defined(__linux) if (cfg->addr.type == QSE_NWAD_NX && cfg->mask.type == QSE_NWAD_NX) { /* access /proc/net/if_inet6 */ read_proc_net_if_inet6 (cfg, ifr); } #endif if ((cfg->flags & QSE_NWIFCFG_BCAST) && ioctl (s, SIOCGIFBRDADDR, ifr) >= 0) { qse_skadtonwad (&ifr->ifr_broadaddr, &cfg->bcast); } if ((cfg->flags & QSE_NWIFCFG_PTOP) && ioctl (s, SIOCGIFDSTADDR, ifr) >= 0) { qse_skadtonwad (&ifr->ifr_dstaddr, &cfg->ptop); } #if defined(SIOCGIFHWADDR) if (ioctl (s, SIOCGIFHWADDR, ifr) >= 0) { QSE_MEMCPY (cfg->ethw, ifr->ifr_hwaddr.sa_data, QSE_SIZEOF(cfg->ethw)); } #elif defined(MACIOC_GETADDR) { /* sco openserver * use the streams interface to get the hardware address. */ int strfd; qse_mchar_t devname[QSE_COUNTOF(ifr->ifr_name) + 5 + 1] = QSE_MT("/dev/"); qse_mbscpy (&devname[5], ifr->ifr_name); if ((strfd = QSE_OPEN (devname, O_RDONLY, 0)) >= 0) { qse_uint8_t buf[QSE_SIZEOF(cfg->ethw)]; struct strioctl strioc; strioc.ic_cmd = MACIOC_GETADDR; strioc.ic_timout = -1; strioc.ic_len = QSE_SIZEOF (buf); strioc.ic_dp = buf; if (ioctl (strfd, I_STR, (char *) &strioc) >= 0) QSE_MEMCPY (cfg->ethw, buf, QSE_SIZEOF(cfg->ethw)); QSE_CLOSE (strfd); } } #endif return 0; #else /* TODO */ return -1; #endif } static void get_moreinfo (int s, qse_nwifcfg_t* cfg, struct ifreq* ifr) { #if defined(ETHTOOL_GLINK) { /* get link status */ struct ethtool_value ev; QSE_MEMSET (&ev, 0, QSE_SIZEOF(ev)); ev.cmd= ETHTOOL_GLINK; ifr->ifr_data = &ev; if (ioctl (s, SIOCETHTOOL,ifr) >= 0) cfg->flags |= ev.data? QSE_NWIFCFG_LINKUP: QSE_NWIFCFG_LINKDOWN; } #endif #if 0 #if defined(ETHTOOL_GSTATS) { /* get link statistics */ struct ethtool_drvinfo drvinfo; drvinfo.cmd = ETHTOOL_GDRVINFO; ifr->ifr_data = &drvinfo; if (ioctl (s, SIOCETHTOOL, ifr) >= 0) { struct ethtool_stats *stats; qse_uint8_t buf[1000]; /* TODO: make this dynamic according to drvinfo.n_stats */ stats = buf; stats->cmd = ETHTOOL_GSTATS; stats->n_stats = drvinfo.n_stats * QSE_SIZEOF(stats->data[0]); ifr->ifr_data = (caddr_t) stats; if (ioctl (s, SIOCETHTOOL, ifr) >= 0) { for (i = 0; i < drvinfo.n_stats; i++) { qse_printf (QSE_T(">>> %llu \n"), stats->data[i]); } } } } #endif #endif } /* TOOD: consider how to handle multiple IPv6 addresses on a single interfce. * consider how to get IPv4 addresses on an aliased interface? so mutliple ipv4 addresses */ int qse_getnwifcfg (qse_nwifcfg_t* cfg) { #if defined(_WIN32) /* TODO */ return -1; #elif defined(__OS2__) /* TODO */ return -1; #elif defined(__DOS__) /* TODO */ return -1; #else int x = -1, s = -1; struct ifreq ifr; qse_size_t ml, wl; if (cfg->type == QSE_NWIFCFG_IN4) { #if defined(AF_INET) s = socket (AF_INET, SOCK_DGRAM, 0); #endif } else if (cfg->type == QSE_NWIFCFG_IN6) { #if defined(AF_INET6) s = socket (AF_INET6, SOCK_DGRAM, 0); #endif } if (s <= -1) return -1; if (cfg->name[0] == QSE_T('\0')&& cfg->index >= 1) { /* TODO: support lookup by ifindex */ } QSE_MEMSET (&ifr, 0, sizeof(ifr)); #if defined(QSE_CHAR_IS_MCHAR) qse_mbsxcpy (ifr.ifr_name, QSE_SIZEOF(ifr.ifr_name), cfg->name); #else ml = QSE_COUNTOF(ifr.ifr_name); if (qse_wcstombs (cfg->name, &wl, ifr.ifr_name, &ml) <= -1) goto oops; #endif x = get_nwifcfg (s, cfg, &ifr); if (x >= 0) get_moreinfo (s, cfg, &ifr); oops: QSE_CLOSE (s); return x; #endif }