The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

/* ********************************************************************	*
 * Interface.xs		version 1.06	9-21-16				*
 *									*
 *     COPYRIGHT 2008-2010 Michael Robinton <michael@bizsystems.com>	*
 *									*
 * This program is free software; you can redistribute it and/or modify	*
 * it under the terms of either:					*
 *									*
 *  a) the GNU General Public License as published by the Free		*
 *  Software Foundation; either version 2, or (at your option) any	*
 *  later version, or							*
 *									*
 *  b) the "Artistic License" which comes with this distribution.	*
 *									*
 * This program 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 either	*
 * the GNU General Public License or the Artistic License for more 	*
 * details.								*
 *									*
 * You should have received a copy of the Artistic License with this	*
 * distribution, in the file named "Artistic".  If not, I'll be glad 	*
 * to provide one.							*
 *									*
 * You should also have received a copy of the GNU General Public 	*
 * License along with this program in the file named "Copying". If not, *
 * write to the 							*
 *									*
 *	Free Software Foundation, Inc.					*
 *	59 Temple Place, Suite 330					*
 *	Boston, MA  02111-1307, USA					*
 *									*
 * or visit their web page on the internet at:				*
 *									*
 *	http://www.gnu.org/copyleft/gpl.html.				*
 * ********************************************************************	*/

#ifdef __cplusplus
extern "C" {
#endif

#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#include "localconf.h"

#ifdef __cplusplus
}
#endif

#include "inet_aton.c"
#include "ni_XStabs_inc.c"

extern const ni_iff_t ni_lx_type2txt[];
const ni_iff_t ni_lx_scope_txt[] = {
	{ RFC2373_GLOBAL,	"global-scope" },
	{ RFC2373_ORGLOCAL,	"org-local" },
	{ RFC2373_SITELOCAL,	"site-local" },
	{ RFC2373_LINKLOCAL,	"link-local" },
	{ RFC2373_NODELOCAL,	"loopback" },
	{ LINUX_COMPATv4,	"lx-compat-v4" }
};

/*
 # make a GV from scratch and return a reference - see gv.c newGVgen
 # delete the GV's name from the stash so it is inaccessible
 #
 # originally written with "rv = sv_newmortal()", now caller must mortalize
 */
#define NI_newGV_ref(rv,gv,stash,tmpstash) \
	rv = newSV(0); \
	gv = gv_fetchpv(Perl_form(aTHX_ "%s::_ifa::_IF_DEV_%ld",HvNAME((HV *)stash),(long)PL_gensym++),TRUE, SVt_PVGV); \
	GvSV(gv) = newSV(0); \
	GvHV(gv) = newHV(); \
	sv_setsv(rv, sv_bless(newRV_noinc((SV*)gv), stash)); \
	tmpstash = GvSTASH(gv); \
	(void)hv_delete(tmpstash, GvNAME(gv), GvNAMELEN(gv), G_DISCARD)

#define niKEYsz 4

#define NI_REF_CHECK(ref) \
        if (!SvROK (ref) || !SvOBJECT (SvRV(ref)) || !SvREADONLY (SvRV(ref))) \
          croak ("Can't call method \"%s\" without a valid object reference", GvNAME (CvGV (cv)));

	/* ********************************************	*
	 *  The information for each interface (IF) is	*
	 *  contained in an HV. The name slot of the	*
	 *  HV holds the IF name. The args slot points	*
	 *  to a hash whose key values represent the	*
	 *  last interrogated state of the IF.		*
	 *						*
	 *   HV {					*
	 *	   indx =>  IV				*
	 *	   flav	=>  IV,				*
	 *	   name	=>  interface name;		*
	 *	   args	=>  {				*
	 *		maci	=> bin string,		*
	 *		mtui	=> IV,			*
	 *		metk	=> IV,			*
	 *		flag	=> NV,			*
	 *		afk	=> {			*
	 *			size	=> IV,		*
	 *			addr	=> [],		*
	 *			netm	=> [],		*
	 *			dsta	=> [],		*
	 *		},				*
	 *		afk	=> {			*
	 *			size	=> IV,		*
	 *			addr	=> [],		*
	 *			netm	=> [],		*
	 *			dsta	=> [],		*
	 *		},				*
	 *	    }					*
	 *	};					*
	 *  Note: for ease of coding, all keys=4 chars	*
	 *	  except for 'afk' which is computed	*
	 * ********************************************	*/

static u_int32_t
afk_len(u_int af, char * key)
{
    sprintf(key,"%d",af);
    return (u_int32_t)strlen(key);
}

static int
af_common(HV * hface, HV * family, struct ifaddrs * ifap, int offset, int addrsz, int *fd, u_int af, int flavor)
{
	struct nifreq ifr;
	char afk[16], * addrptr;
	const char * addr = "addr", * netmask = "netm", * dstaddr = "dsta";
	const char * flags = "flag", * size = "size", * mtu = "mtui", * metric = "metk", * ifindex = "indx";
	int i;
	AV * anyaddr;
	u_int64_t fgs;
	struct ni_ifconf_flavor * nifp = ni_ifcf_get(flavor);
#ifdef HAVE_STRUCT_LIFREQ
	struct lifreq lifr;
#endif

/*	is status needed or has it been saved already? if not, save flags, mtu, metric	*/
		if (! hv_exists(hface,flags,niKEYsz)) {
/*	flags		*/
		    fgs = ifap->ifa_flags;
#ifdef HAVE_STRUCT_LIFREQ
		    if (flavor == NI_LIFREQ) {
			strlcpy(lifr.lifr_name,ifap->ifa_name,IFNAMSIZ);
			if ((*fd = ni_clos_reopn_dgrm(*fd,af)) < 0)
	        	    goto no_xflags;
        		if (ioctl(*fd,nifp->siocgifflags,&lifr) != -1)
        		    fgs = lifr.lifr_flags;
		    }
    	no_xflags:
#endif
		    hv_store(hface,flags,niKEYsz,newSVnv(fgs),0);
		    if ((*fd = ni_clos_reopn_dgrm(*fd,AF_INET)) < 0)
			return -1;

		    strlcpy(ifr.ni_ifr_name,ifap->ifa_name,IFNAMSIZ);
/*	save MTU	*/
		    if ((i = ni_get_any(*fd,nifp->siocgifmtu,&ifr)) < 0)
			i = 0;
		    hv_store(hface,mtu,niKEYsz,newSViv(i),0);
/*	save METRIC	*/
		    if ((i = ni_get_any(*fd,nifp->siocgifmetric,&ifr)) < 0)
			i = 0;
		    hv_store(hface,metric,niKEYsz,newSViv(i),0);
/*	save INDEX if defined for this platform	*/
		    if (nifp->siocgifindex != 0) {
			if ((i = ni_get_any(*fd,nifp->siocgifindex,&ifr)) < 0)
			    i = -1;
			hv_store(hface,ifindex,niKEYsz,newSViv(i),0);
		    }
		    if ((*fd = ni_clos_reopn_dgrm(*fd,af)) < 0)
			return -1;
		}

/*	if the address arrays are populated, get pointer	*/
		if (hv_exists(hface,afk,afk_len(af,afk)))
		    family = (HV*)SvRV(*hv_fetch(hface,afk,afk_len(af,afk),0));
		else {
		    family = newHV();
		    hv_store(hface,afk,afk_len(af,afk),newRV_noinc((SV*)family),0);
/*	save address size for user	*/
		    hv_store(family,size,niKEYsz,newSViv(addrsz),0);
/*	populate the address arrays	*/
		    hv_store(family,addr,niKEYsz,   newRV_noinc((SV*)newAV()),0);
		    hv_store(family,netmask,niKEYsz,newRV_noinc((SV*)newAV()),0);
		    hv_store(family,dstaddr,niKEYsz,newRV_noinc((SV*)newAV()),0);
		}
/*	addr	*/
		anyaddr = (AV*)SvRV(*hv_fetch(family,addr,niKEYsz,0));
		if (ifap->ifa_addr == NULL)
		    av_push(anyaddr,newSV(0));
		else {
#ifdef LOCAL_SIZEOF_SOCKADDR_IN6
		    if (af == AF_INET6)
	/* waste the scopeid if KAME	*/
			(void) ni_get_scopeid((struct sockaddr_in6 *)(ifap->ifa_addr));
#endif	
		    addrptr = ((char *)ifap->ifa_addr) + offset;
		    av_push(anyaddr,newSVpvn(addrptr,addrsz));
		}
/*	netmask	*/
		anyaddr = (AV*)SvRV(*hv_fetch(family,netmask,niKEYsz,0));
		if (ifap->ifa_netmask == NULL)
		    av_push(anyaddr,newSV(0));
		else {
		    addrptr = ((char *)ifap->ifa_netmask) + offset;
		    av_push(anyaddr,newSVpvn(addrptr,addrsz));
		}
/*	dstaddr	*/
		anyaddr = (AV*)SvRV(*hv_fetch(family,dstaddr,niKEYsz,0));
		if (ifap->ifa_dstaddr == NULL)
		    av_push(anyaddr,newSV(0));
		else {
		    addrptr = ((char *)ifap->ifa_dstaddr) + offset;
		    av_push(anyaddr,newSVpvn(addrptr,addrsz));
		}
		return 0;
}

static int
getheifs(SV ** sp, I32 ax, I32 items, SV * ref, HV * stash, int ix, char * keyname)
{
	int flavor, forcflavor, i, fd = -1, n = 1, ic, need_mac_addr = 1;
	u_int af;
	struct ifaddrs * ifap = NULL, * ifapbase = NULL;
	const char * mac = "maci", * name = "name", * args = "args", * flags = "flag", * flav = "flav", * ifindex = "indx";
	char nbuf[IFNAMSIZ], afk[16];
	u_char * macp;
	struct nifreq ifr;
	struct ni_lnk_names {
	    char niln_name[IFNAMSIZ];
	    int niln_len;
	} name_link, * nlbase = NULL, * nl;
	struct sockaddr_in sin;
#ifdef LOCAL_SIZEOF_SOCKADDR_IN6
	struct sockaddr_in6 sin6;
#endif
#ifdef LOCAL_SIZEOF_SOCKADDR_DL
	struct sockaddr_dl * sadl;
#elif defined LOCAL_SIZEOF_SOCKADDR_LL
	struct sockaddr_ll *sall;
#endif  
	STRLEN	len;
	I32	klen;
	SV * rv;
	GV * gvface;
	AV * anyaddr;
	HV * ifaces, * hface, * family, * wrapr, * owrap;

/* if new	*/
	if (keyname != NULL) {		/* this is a redo, not IF rqst	*/
	    klen = strlen(keyname);
	    owrap = (HV *)SvRV(ref);
	}
	else if (ix == 1) {
	    if (items == 1) {		/* refresh of existing IF	*/
		if (!SvROK(ref))
		    goto iferror1;
		owrap = (HV *)SvRV(ref);
		if (! hv_exists(owrap,name,niKEYsz))
		    goto iferror1;
		rv = *hv_fetch(owrap,name,niKEYsz,0);
		if (!SvPOK(rv))
		    goto iferror1;
		strlcpy(nbuf,SvPV(rv,len),len +1);
		keyname = nbuf;
		klen = len;
	    }
	    else if (items == 2) {	/* new or update IF w/ hash ref	*/
		if (! SvPOK(ST(1)))
		    goto iferror1;
		keyname = SvPV(ST(1),len);
		klen = len;
	    }
	    else {			/* new or update IF with hash	*/
		goto iferror1;		/* not yet implemented		*/
	    }
	}
/* get the data about interfaces from the default getifaddrs	*/
	forcflavor = flavor = ni_getifaddrs(&ifap, 0);
	if (flavor < 0)
	    goto iferror1;
	else if (flavor == 0)
	    forcflavor = NI_IFREQ;
/* belt and suspenders test for failed fetch of ifaddrs data	*/
	if (ifap == NULL)		/* there are no addresses to check */
	    goto iferror1;

	ifapbase = ifap;

/*
 *	hash ifaces is for temporary aggregation by interface
 *	seems that a plain hv_undef at the end leaks
 *	mortalization stops the leak
 *	decrementing the REFCNT instead causes a segfault, puzzling!
 */
	ifaces = (HV*)sv_2mortal((SV*)newHV());
/*	ifaces = newHV();
 *	SvREFCNT_dec(ifaces);
 *
 *	storage conventions
 *
 *	ifaces	=> temporary interface hash keyed by name
 *	hface	=> hash for a particular interface
 *	family	=> family within an hface
 *	anyaddr	=> an address array within a family
 */

	if ((nl = nlbase = (struct ni_lnk_names *)calloc(1,sizeof(struct ni_lnk_names))) == NULL)
	    goto iferror2;

	while (ifap != NULL) {
	    len = strlen(ifap->ifa_name);
/* this is sub 'new', target only the 'name'	*/
	    if (ix && strncmp(ifap->ifa_name,keyname,len))
		goto nextname;
/* interface is known?				*/
	    if (hv_exists(ifaces,ifap->ifa_name,len))
/* fetch reference to interface ref		*/
		hface = (HV*)SvRV(*hv_fetch(ifaces,ifap->ifa_name,len,0));
	    else {
/* skip invalids */
		if (ifap->ifa_addr == NULL) goto nextname;
/* create an HV to hold this interface		*/
		hface = newHV();
/* and insert ref into unique/tmp storage hash	*/
		hv_store(ifaces,ifap->ifa_name,len,newRV_noinc((SV*)hface),0);
/* add name to the list to preserve kernel ordering
 * can use memcpy here because struct is bzero'd and will have terminating null
 */
		memcpy(nl->niln_name,ifap->ifa_name,len);
		nl->niln_len = len;
		n++;
		if ((nl = (struct ni_lnk_names *)realloc(nlbase,n * sizeof(struct ni_lnk_names))) == NULL)
		    goto iferror2;
		nlbase = nl;
		nl = nlbase + n -1;
		bzero(nl,sizeof(struct ni_lnk_names));
	    }

/* check for improperly truncated entry		*/
	    if (ifap->ifa_addr == NULL)
		goto iferror2;
/*
 * only store 'ifa_flags' for families where we store the address since
 * we don't know if 'ifa_flags' is consistently populated for link/packet
 */
	    af = ((struct sockaddr *)(ifap->ifa_addr))->sa_family;
/* AF_INET					*/
	    if (af == AF_INET) {
		if (af_common(hface,family,ifap,
/*			(u_char *)&((struct sockaddr_in *)ifap->ifa_addr)->sin_addr, */
			((char *)&sin.sin_addr) - (char *)&sin,
			sizeof(struct in_addr),&fd,af,forcflavor) != 0) {
    iferror3:
		    free(ifapbase);
		    goto iferror2;
		}
	    }

#ifdef LOCAL_SIZEOF_SOCKADDR_IN6
/* AF_IFNET6					*/
	    else if (af == AF_INET6) {
		if (af_common(hface,family,ifap,
/*			(u_char *)&((struct sockaddr_in6 *)ifap->ifa_addr)->sin6_addr, */
			((char *)&sin6.sin6_addr) - (char *)&sin6,
			sizeof(struct in6_addr),&fd,af,forcflavor) != 0)
		    goto iferror3;
	    }

#endif	/* SOCKADDR_IN6	*/
#ifdef LOCAL_SIZEOF_SOCKADDR_DL

	    else if (af == AF_LINK) {
		if (ifap->ifa_addr != NULL) {
		    sadl = (struct sockaddr_dl *)ifap->ifa_addr;
		    macp = (unsigned char *)(sadl->sdl_data + sadl->sdl_nlen);
		    if (NI_MAC_NOT_ZERO(macp)) {
			need_mac_addr = 0;
			hv_store(hface,mac,niKEYsz,newSVpvn((char *)macp,6),0);
		    }
		    if (sadl->sdl_index != 0 && (! hv_exists(hface,ifindex,niKEYsz)))
			hv_store(hface,ifindex,niKEYsz,newSViv(sadl->sdl_index),0);
		}
	    }

#elif defined LOCAL_SIZEOF_SOCKADDR_LL

	    else if (af == AF_PACKET) {
		if (ifap->ifa_addr != NULL) {
		    sall = (struct sockaddr_ll *)ifap->ifa_addr;
		    macp = (unsigned char *)sall->sll_addr;
		    if (NI_MAC_NOT_ZERO(macp)) {
			need_mac_addr = 0;
			hv_store(hface,mac,niKEYsz,newSVpvn((char *)macp,6),0);
		    }
		    if (sall->sll_ifindex != 0 && (! hv_exists(hface,ifindex,niKEYsz)))
			hv_store(hface,ifindex,niKEYsz,newSViv(sall->sll_ifindex),0);
		}
	    }

#endif

    nextname:
	    ifap = ifap->ifa_next;
	}
	close(fd);
	ni_free_gifa(ifapbase,flavor);
	nl = nlbase;
	if (GIMME == G_ARRAY)
	    i = 0;
	else
	    i = -1;

	while(nl->niln_name[0] != '\0') {
	    if (hv_exists(ifaces,nl->niln_name,nl->niln_len)) {
		if (ix < 2) {		/* if this is a redo skip wrapr operation	*/
		    wrapr = newHV();
		    hv_store(wrapr,name,niKEYsz,newSVpvn((char *)nl->niln_name,nl->niln_len),0);
		    hv_store(wrapr,flav,niKEYsz,newSViv(flavor),0);
		}
		rv = hv_delete(ifaces,nl->niln_name,nl->niln_len,0);
		if (need_mac_addr) {
#ifdef __ni_Linux
/* do not add mac to alias's				*/
		    if (index(nl->niln_name,':') != NULL)
			goto no_mac;
#endif
/* skip this interface if it is the loopback device	*/
		    hface = (HV*)SvRV(rv);
		    if (IFF_LOOPBACK & (u_int64_t)SvNV((SV*)(*hv_fetch(hface,flags,niKEYsz,0))))
			goto no_mac;
		    if (hv_exists(hface,afk,afk_len(AF_INET,afk)))
			    goto mac_continue;
#ifdef LOCAL_SIZEOF_SOCKADDR_IN6
		    else if(hv_exists(hface,afk,afk_len(AF_INET6,afk)))
			    goto mac_continue;
#endif
		    else
			goto no_mac;
    mac_continue:
		    strlcpy(ifr.ni_ifr_name,nl->niln_name,IFNAMSIZ);
		    if ((macp = ni_fallbackhwaddr(af,&ifr)) != NULL)
			hv_store(hface,mac,niKEYsz,newSVpvn((char *)macp,6),0);
		}
    no_mac:

/* don't want the ref to be mortal, only the wrapper	*/
		SvREFCNT_inc(rv);
		if (ix > 1)
		    hv_store(owrap,args,niKEYsz,rv,0);
		else {
		    hv_store(wrapr,args,niKEYsz,rv,0);
		    XPUSHs(sv_2mortal(sv_bless(newRV_noinc((SV*)wrapr),stash)));
		}
	    	if (i < 0) {
		    i = 1;
		    break;
		}
		i++;
		nl++;
	    }
/* should never do the else!				*/
	    else {
    iferror2:
		free(nlbase);
		hv_undef(ifaces);
    iferror1:
		return -1;
	    }
	}
	free(nlbase);
	hv_undef(ifaces);
	return i;
}

/*	return NULL or pointer to first available address	*/
static SV *
get_first_address(SV * ref, char * key, int sixonly)
{
    HV * hv, * family;
    AV * av;
    SV * sv;
    char afk[16], * args = "args";

    hv = (HV *)SvRV(ref);		/* wrapper	*/
    if (! hv_exists(hv,args,niKEYsz))
	return NULL;
    sv = *hv_fetch(hv,args,niKEYsz,0);	/* hface	*/
    if (!SvROK(sv))
	return NULL;
    hv = (HV *)SvRV(sv);
    if (! sixonly && hv_exists(hv,afk,afk_len(AF_INET,afk)))
	family = (HV*)SvRV(*hv_fetch(hv,afk,afk_len(AF_INET,afk),0));
#ifdef LOCAL_SIZEOF_SOCKADDR_IN6
    else if (hv_exists(hv,afk,afk_len(AF_INET6,afk)))
	family = (HV*)SvRV(*hv_fetch(hv,afk,afk_len(AF_INET6,afk),0));
#endif
    else
	return NULL;
    av = (AV*)SvRV(*hv_fetch(family,key,niKEYsz,0));
    return *av_fetch(av,0,0);		/* first addy	*/
}


MODULE = Net::Interface	PACKAGE = Net::Interface PREFIX = NIP_

INCLUDE: miniSocketXS.c


void
interfaces(ref,...)
	SV * ref
    PROTOTYPE: $;$
    ALIAS:
	new = 1
    PREINIT:
	HV * stash = SvROK (ref)
		? SvSTASH (SvRV (ref)) : gv_stashsv (ref, 0);
	int rv;
    PPCODE:
	if ((rv = getheifs(sp,ax,items,ref,stash,(int)ix,NULL)) < 0) {
	    if (GIMME == G_ARRAY)
		XSRETURN_EMPTY;
	    else
		XSRETURN_UNDEF;
	}
	XSRETURN(rv);


void
dtest(ref)
	SV * ref
    PREINIT:
	char * myname = "my name", * one = "one", * two = "two", * array = "array";
	SV * rv, *arv, *mn;
	GV * gv;
	AV * av;
	HV * stash, * tstash;
    PPCODE:
	stash = SvROK (ref) ? SvSTASH (SvRV (ref)) : gv_stashsv (ref, 0);
	NI_newGV_ref(rv,gv,stash,tstash);
 #	mn = newSVpv(myname,0);
 #	GvSV(gv) = mn;
 #	hv_store(GvHV(gv),one,strlen(one),newSViv(1),0);
 #	hv_store(GvHV(gv),two,strlen(two),newSViv(2),0);
 #	av = newAV();
 #	arv = newRV_noinc((SV *)av);
 #	av = (AV*)SvRV(*hv_store(GvHV(gv),array,strlen(array),arv,0));
 #	av_push(av,newSViv(55));
	XPUSHs(sv_2mortal(rv));
	XSRETURN(1);


void
dtest2(ref)
	SV * ref
    PREINIT:
	SV * sv, * pv;
	HV * hv;
	char * name = "Sv Name";
	char * n2 = "LOGO";
	char * myname = "my name", * one = "one", * two = "two", * array = "array";
    PPCODE:
	hv = newHV();
	hv_store(hv,one,strlen(one),newSViv(1),0);
	hv_store(hv,two,strlen(two),newSViv(2),0);
	sv = (SV*)newRV_noinc((SV*)hv);
	XPUSHs(sv_2mortal(sv));
	XSRETURN(1);


void
__developer(ref)
	SV *ref
    ALIAS:
	d_ni_ifreq	= NI_IFREQ
	d_ni_lifreq	= NI_LIFREQ
	d_ni_in6_ifreq	= NI_IN6_IFREQ
	d_ni_linuxproc	= NI_LINUXPROC
    PREINIT:
	char * process;
	int er = ni_developer(ix);
    CODE:
	if (er == 0)
	    XSRETURN_EMPTY;

	switch (ix) {
	case NI_IFREQ :
	    process = "NI_FREQ";
	    break;
	case NI_LIFREQ :
	    process = "NI_LIFREQ";
	    break;
	case NI_IN6_IFREQ :
	    process = "NI_IN6_IFREQ";
	    break;
	case NI_LINUXPROC :
	    process = "NI_LINUXPROC";
	    break;
	default :
	    process = "UNDEFINED";
	}
	printf("%s: %s\n",process,strerror(er));


void
gifaddrs_base(ref)
	SV * ref
    ALIAS:
 #	base		= 0
	gifa_ifreq	= NI_IFREQ
	gifa_lifreq	= NI_LIFREQ
	gifa_in6_ifreq	= NI_IN6_IFREQ
	gifa_linuxproc	= NI_LINUXPROC
    PREINIT:
	struct ifaddrs * ifap;
	int rv;
    CODE:
	if ((rv = ni_getifaddrs(&ifap,ix)) == -1) {
	    printf("failed PUNT!\n");
	    XSRETURN_EMPTY;
	}
	ni_getifaddrs_dump(rv,ifap);
	ni_free_gifa(ifap,rv);


void
cidr2mask(prefix, size)
	int	prefix
	int	size
    PREINIT:
	unsigned char mask[16];
    PPCODE:
	if (!(size == 4 || size == 16))
	    croak("Bad arg for %s, requested mask size is %d, should be 4 or 16",
			GvNAME (CvGV (cv)),size);
	if (prefix < 0 || prefix > (size * 8))
	    croak ("Bad arg for %s, mask length is %d, should be 0 to <= %d",
			GvNAME (CvGV (cv)),size * 8);

	ni_plen2mask(mask,prefix,size);
	XPUSHs(sv_2mortal(newSVpvn((char *)mask,size)));
	XSRETURN(1);


int
mask2cidr(ref,...)
	SV * ref;
    PROTOTYPE: $;$
    PREINIT:
	unsigned char * mp;
	STRLEN len;
	char * netmask = "netm";
	SV * sv;
    CODE:
 #	called as method with argument
	if (items == 2)
	    mp = (unsigned char *)SvPV(ST(1),len);
 #	called as a function
	else if (! SvROK(ref))
	    mp = (unsigned char *)SvPV(ST(0),len);
 #	called as method
	else {
	    if ((sv = get_first_address(ref,netmask,0)) == NULL)
		len = 0;
	    else
		mp = (unsigned char *)SvPV(sv,len);
	}
	if (!(len == 4 || len == 16))
	    croak("Bad arg length for %s, mask length is %d, should be 4 or 16",
			GvNAME (CvGV (cv)),len);
	RETVAL = ni_prefix(mp,len);
    OUTPUT:
	RETVAL


void
NIP_type(ref,...)
	SV * ref
    PROTOTYPE: $;$
    ALIAS:
	scope = 1
    PREINIT:
	unsigned char * s6bytes;
	char * addr = "addr";
	UV type;
	STRLEN len;
	HV * hv;
	SV * sv;
    PPCODE:
 #	called as method with argument
	if (items == 2)
	    s6bytes = (unsigned char *)SvPV(ST(1),len);
 #	called as a function
	else if (! SvROK(ref))
	    s6bytes = (unsigned char *)SvPV(ST(0),len);
 #	called as method
	else {
	    if ((sv = get_first_address(ref,addr,1)) == NULL)
		len = 0;
	    else
		s6bytes = (unsigned char *)SvPV(sv,len);
	}
	if (! len == 16)
	    croak("Bad arg length for %s, address length is %d, should be 16",
			GvNAME (CvGV (cv)),len);

	type = ni_in6_classify(s6bytes);

	if (ix == 0)
	    XPUSHs(sv_2mortal(newSVuv(type)));
	else
	    XPUSHs(sv_2mortal(newSViv(ni_lx_type2scope((int)type))));

	XSRETURN(1);


void
mac_bin2hex(ref,...)
	SV * ref
    PROTOTYPE: $;$
    PREINIT:
	unsigned char * macbin;
	char macbuf[18], * format, * args = "args", * mac = "maci";
	STRLEN len;
	HV * hv;
	SV * sv;
    PPCODE:
 #	called as method with argument
	if (items == 2)
	    macbin = (unsigned char *)SvPV(ST(1),len);
 #	called as a function
	else if (! SvROK(ref))
	    macbin = (unsigned char *)SvPV(ST(0),len);
 #	called as method
	else {
	    hv = (HV *)SvRV(ref);
	    if (! hv_exists(hv,args,niKEYsz))
		XSRETURN_UNDEF;
	    sv = *hv_fetch(hv,args,niKEYsz,0);
	    if (!SvROK(sv))
		XSRETURN_UNDEF;
	    hv = (HV *)SvRV(sv);
	    if (! hv_exists(hv,mac,niKEYsz))
		XSRETURN_UNDEF;
	    sv = *hv_fetch(hv,mac,niKEYsz,0);
	    if (! SvPOK(sv))
		XSRETURN_UNDEF;
	    macbin = (unsigned char *)SvPV(sv,len);
	}
	if (len != 6)
	    croak("Bad arg length for %s, MAC length is %d, should be 6",
			GvNAME (CvGV (cv)),len);

	format = SvPV(get_sv("Net::Interface::mac_format", FALSE),len);
	sprintf(macbuf,format,
		macbin[0],macbin[1],macbin[2],macbin[3],macbin[4],macbin[5]);

	XPUSHs(sv_2mortal(newSVpv(macbuf,0)));
	XSRETURN(1);


void
full_inet_ntop(neta)
	SV * neta
    PREINIT:
	unsigned char * naddr;
	char mask[40], * format;
	STRLEN len;
    PPCODE:
	naddr = (unsigned char *)SvPV(neta,len);
	if (len != 16)
	    croak("Bad arg length for %s, ipV6 length is %d, should be 16 bytes",
			GvNAME (CvGV (cv)),len);

	format = SvPV(get_sv("Net::Interface::full_format", FALSE),len);
	sprintf(mask,format,
		naddr[0],naddr[1],naddr[2],naddr[3],
		naddr[4],naddr[5],naddr[6],naddr[7],
		naddr[8],naddr[9],naddr[10],naddr[11],
		naddr[12],naddr[13],naddr[14],naddr[15]);

	XPUSHs(sv_2mortal(newSVpvn((char *)mask,39)));
	XSRETURN(1);


void
_lx_types()
    ALIAS:
	IPV6_ADDR_ANY			= IPV6_ADDR_ANY
	IPV6_ADDR_UNICAST		= IPV6_ADDR_UNICAST
	IPV6_ADDR_MULTICAST		= IPV6_ADDR_MULTICAST
	IPV6_ADDR_ANYCAST		= IPV6_ADDR_ANYCAST
	IPV6_ADDR_LOOPBACK		= IPV6_ADDR_LOOPBACK
	IPV6_ADDR_LINKLOCAL		= IPV6_ADDR_LINKLOCAL
	IPV6_ADDR_SITELOCAL		= IPV6_ADDR_SITELOCAL
	IPV6_ADDR_COMPATv4		= IPV6_ADDR_COMPATv4
	IPV6_ADDR_SCOPE_MASK		= IPV6_ADDR_SCOPE_MASK
	IPV6_ADDR_MAPPED		= IPV6_ADDR_MAPPED
	IPV6_ADDR_RESERVED		= IPV6_ADDR_RESERVED
	IPV6_ADDR_ULUA			= IPV6_ADDR_ULUA
	IPV6_ADDR_6TO4			= IPV6_ADDR_6TO4
	IPV6_ADDR_6BONE			= IPV6_ADDR_6BONE
	IPV6_ADDR_AGU			= IPV6_ADDR_AGU
	IPV6_ADDR_UNSPECIFIED		= IPV6_ADDR_UNSPECIFIED
	IPV6_ADDR_SOLICITED_NODE	= IPV6_ADDR_SOLICITED_NODE
	IPV6_ADDR_ISATAP		= IPV6_ADDR_ISATAP
	IPV6_ADDR_PRODUCTIVE		= IPV6_ADDR_PRODUCTIVE
	IPV6_ADDR_6TO4_MICROSOFT	= IPV6_ADDR_6TO4_MICROSOFT
	IPV6_ADDR_TEREDO		= IPV6_ADDR_TEREDO
	IPV6_ADDR_ORCHID		= IPV6_ADDR_ORCHID
	IPV6_ADDR_NON_ROUTE_DOC		= IPV6_ADDR_NON_ROUTE_DOC
    PREINIT:
	SV * rv;
	int n, i;
    PPCODE:
	rv = sv_2mortal(newSViv(ix));
	n = ni_sizeof_type2txt();
	for (i=0; i<n; i++) {
	    if (ni_lx_type2txt[i].iff_val == ix) {
		sv_setpv(rv,ni_lx_type2txt[i].iff_nam);
		break;
	    }
	}
	SvIOK_on(rv);
	XPUSHs(rv);
	XSRETURN(1);


void
_lx_scope()
    ALIAS:
	RFC2373_GLOBAL		= RFC2373_GLOBAL
	RFC2373_ORGLOCAL	= RFC2373_ORGLOCAL
	RFC2373_SITELOCAL	= RFC2373_SITELOCAL
	RFC2373_LINKLOCAL	= RFC2373_LINKLOCAL
	RFC2373_NODELOCAL	= RFC2373_NODELOCAL
	LINUX_COMPATv4		= LINUX_COMPATv4
    PREINIT:
	SV * rv;
	int n, i;
    PPCODE:
	rv = sv_2mortal(newSViv(ix));
	n = sizeof(ni_lx_scope_txt) / sizeof(ni_iff_t);
	for (i=0; i<n; i++) {
	    if (ni_lx_scope_txt[i].iff_val == ix) {
		sv_setpv(rv,ni_lx_scope_txt[i].iff_nam);
		break;
	    }
	}
	SvIOK_on(rv);
	XPUSHs(rv);
	XSRETURN(1);


size_t
NIP_strlcpy(...)
    PROTOTYPE: $$$
    PREINIT:
	char * d = NULL;
	char * s = SvPV_nolen(ST(1));
	size_t size = (size_t)SvIV(ST(2));
    CODE:
	if ((int)size > 0) {
	    d = New(1234,d,2 * size,char);
	    memset(d,'X',2 * size);
	    *(d + (2*size) -1) = 0;
	    RETVAL = strlcpy(d,s,size);
	    sv_setpv(ST(0),d);
	    Safefree(d);
	} else
	    RETVAL = 0;
    OUTPUT:
	RETVAL


int
_sets(ref,...)
	SV * ref
    ALIAS:
	mtu	= 0
	metric	= 1
	flags	= 2
	index	= 3
    PREINIT:
	int cmd, fd, rv, flavor;
	struct nifreq ifr, * ofifr;
	struct ni_ifconf_flavor * nifp;
	HV * hv;
	SV * sv;
	char * key, * args = "args", * name = "name", * flav = "flav";
	STRLEN len;
    CODE:
        if (!SvROK (ref) || !SvOBJECT(SvRV(ref)))
	    croak ("Can't call method \"%s\" without a valid object reference", GvNAME (CvGV (cv)));

	if (items > 2) {
    not_found:
	    croak ("Invalid or corrupted arguments passed to \"%s\"", GvNAME (CvGV (cv)));
	}
	hv = (HV *)SvRV(ref);
	if (! hv_exists(hv,name,niKEYsz))
	    goto not_found;
	if (! hv_exists(hv,args,niKEYsz))
	    goto not_found;
	if (! hv_exists(hv,flav,niKEYsz)) {
    no_data:
	    XSRETURN_UNDEF;
	}
	sv = *hv_fetch(hv,name,niKEYsz,0);
	if (!SvPOK(sv))
	    goto no_data;
	strlcpy(ifr.ni_ifr_name,SvPV(sv,len),len +1);
	sv = *hv_fetch(hv,flav,niKEYsz,0);
	if (!SvIOK(sv))
	    goto no_data;
 # flavor and offset if present
	flavor = SvIV(sv);
	nifp = ni_safe_ifcf_get(flavor);
	ofifr = (struct nifreq *)((char *)&ifr + nifp->ifr_offset);

	sv = *hv_fetch(hv,args,niKEYsz,0);
	if (!SvROK(sv))
	    goto no_data;

	switch (ix)
	{
	case 0 :
	    cmd = nifp->siocsifmtu;
	    key = "mtui";
	    break;
	case 1 :
	    cmd = nifp->siocsifmetric;
	    key = "metk";
	    break;
	case 2 :
	    cmd = nifp->siocsifflags;
	    key = "flag";
	    break;
	case 3 :
	    cmd = 0;
	    key = "indx";
	    break;
	default :
	    goto not_found;
	}

 # args hash
	hv = (HV *)SvRV(sv);
	if (! hv_exists(hv,key,niKEYsz))
	    goto no_data;
 # value
	sv = *hv_fetch(hv,key,niKEYsz,0);
	if (ix == 2) {
	    if (! SvNOK(sv))
		goto no_data;
	    RETVAL = SvNV(sv);
	}
	else {
	    if (! SvIOK(sv))
		goto no_data;
	    RETVAL = SvIV(sv);
	}
	if (cmd && items > 1) {
	    if (!(SvIOK(ST(1)) || SvNOK(ST(1))))
		goto no_data;

	    if (flavor == NI_LIFREQ)
		ofifr->ni_uint64 = (u_int64_t)SvNV(ST(1));
	    else
		ifr.ni_uint = (u_int)((u_int64_t)SvNV(ST(1)) & 0x1ffffu);

	    if ((fd = ni_clos_reopn_dgrm(-1,AF_INET)) < 0)
		goto no_data;
	    if ((rv = ni_set_any(fd,cmd,&ifr)) < 0) {
		close(fd);
		goto no_data;
	    }
	    close(fd);
 #	items == 2 and ix == 2 for this call
	    if (getheifs(sp,ax,2,ref,NULL,2,ifr.ni_ifr_name) < 0)
		goto not_found;
	}
    sets_done:
    OUTPUT:
	RETVAL


 # ############################################################	#
 #	Certain broken Solaris headers cause build		#
 #	errors with the syntax for constructors.		#
 #  i.e.							#
 #	void __attribute__((constructor))			#
 #	constructor_function () 				#
 #	{							#
 #		code....					#
 #	};							#
 #								#
 # line 249: syntax error before or at: (			#
 # line 251: warning: old-style declaration or incorrect type	#
 # cc: acomp failed [filename.c]				#
 # *** Error code 2						#
 # make: Fatal error: Command failed for target 'filename.o'	#
 #								#
 #	The various constructors are declared here and called	#
 #	during module load as a work-around to this problem	#
 # ############################################################	#
 
void
conreg()
    CODE:
	ni_ifreq_ctor();
	ni_in6_ifreq_ctor();
	ni_lifreq_ctor();
	ni_linuxproc_ctor();


void
macstuff(dev)
	SV * dev
    PREINIT:
	struct nifreq ifr;
	char * name;
	u_char * x;
	STRLEN len;
    CODE:
	name = SvPV(dev,len);
	strlcpy(ifr.ni_ifr_name,name,IFNAMSIZ);
 	x = ni_fallbackhwaddr(AF_INET,&ifr);
	if (x == NULL)
	    printf("got NULL\n");
	else {
	    NI_PRINT_MAC(x);
	    printf("\n");
	}




INCLUDE: netsymbolXS.inc