700 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			700 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * $Id$
 | |
|  *
 | |
|     Copyright 2006-2014 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 <http://www.gnu.org/licenses/>.
 | |
|  */
 | |
| 
 | |
| #include <qse/cmn/nwif.h>
 | |
| #include <qse/cmn/str.h>
 | |
| #include <qse/cmn/mbwc.h>
 | |
| #include "mem.h"
 | |
| 
 | |
| #if defined(_WIN32)
 | |
| #	include <winsock2.h>
 | |
| #	include <ws2tcpip.h>
 | |
| /*#	include <iphlpapi.h> */
 | |
| #elif defined(__OS2__)
 | |
| #	if defined(TCPV40HDRS)
 | |
| #		define BSD_SELECT
 | |
| #	endif
 | |
| #	include <types.h>
 | |
| #	include <sys/socket.h>
 | |
| #	include <netinet/in.h>
 | |
| #	include <sys/ioctl.h>
 | |
| #	include <nerrno.h>
 | |
| #	if defined(TCPV40HDRS)
 | |
| #		define USE_SELECT
 | |
| #		include <sys/select.h>
 | |
| #	else
 | |
| #		include <unistd.h>
 | |
| #	endif
 | |
| #elif defined(__DOS__)
 | |
| 	/* TODO: */
 | |
| #else
 | |
| #	include "syscall.h"
 | |
| #	include <sys/socket.h>
 | |
| #	include <netinet/in.h>
 | |
| #	if defined(HAVE_SYS_IOCTL_H)
 | |
| #		include <sys/ioctl.h>
 | |
| #	endif
 | |
| #	if defined(HAVE_NET_IF_H)
 | |
| #		include <net/if.h>
 | |
| #	endif
 | |
| #	if defined(HAVE_NET_IF_DL_H)
 | |
| #		include <net/if_dl.h>
 | |
| #	endif
 | |
| #	if defined(HAVE_SYS_SOCKIO_H)
 | |
| #		include <sys/sockio.h>
 | |
| #	endif
 | |
| #	if defined(HAVE_IFADDRS_H)
 | |
| #		include <ifaddrs.h>
 | |
| #	endif
 | |
| #	if defined(HAVE_SYS_SYSCTL_H)
 | |
| #		include <sys/sysctl.h>
 | |
| #	endif
 | |
| #	if defined(HAVE_SYS_STROPTS_H)
 | |
| #		include <sys/stropts.h> /* stream options */
 | |
| #	endif
 | |
| #	if defined(HAVE_SYS_MACSTAT_H)
 | |
| #		include <sys/macstat.h>
 | |
| #	endif
 | |
| #	if defined(HAVE_LINUX_ETHTOOL_H)
 | |
| #		include <linux/ethtool.h>
 | |
| #	endif
 | |
| #	if defined(HAVE_LINUX_SOCKIOS_H)
 | |
| #		include <linux/sockios.h>
 | |
| #	endif
 | |
| #endif
 | |
| 
 | |
| /*
 | |
| 
 | |
| #if defined(HAVE_NET_IF_DL_H)
 | |
| #	include <net/if_dl.h>
 | |
| #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
 | |
| }
 |