Net::I...veloper(3pm)

🌸 Net::Interface::Developer 🌸

okay bestie, so we're reading Net::Interface::Developer(3pm) β€” it's part of the User Contributed Perl Documentation and it's the developer page. like, this is the one you read when you wanna poke at the internals. buckle up 🫢


✨ NAME ✨

Net::Interface::Developer β€” api, notes, hints

πŸ¦‹ DESCRIPTION πŸ¦‹

so basically this page is where all the development notes and API documentation for the Net::Interface module live. and the whole vibe is: please, please help fill in the missing pieces for OS's and address families that currently don't have support yet. it's giving "community project, open invite, no gatekeeping" πŸ’–


πŸ‘‘ ARCHITECTURE πŸ‘‘

okay so the way Net::Interface does its thing: it gathers info about the network interfaces in an OS-independent way. how? it first tries getifaddrs. and if getifaddrs isn't supported on that OS, it falls back to system ioctl's and the ifreq, in6_ifreq, lifreq structures that are defined on the local host.

Linux is a lil different though β€” ipV6 info is only available directly from the kernel on older versions where getifaddrs isn't available. so the ifreq and friends info gets used to generate a getifaddrs response. it's giving "we improvise, we adapt, we slay" πŸ’…

and THIS, bestie, is exactly why the opensource community needs to keep developing this. so many OS's have peculiar ioctl variants and SIOC's variants that need their own unique code solutions. the author is like "I'm sure not all of them are presently included" β€” and honestly? fair.

Net::Interface is built in 5 layers, from the base on up. here's the lineup:

description:   files          code
1) AF_xxx families: ni_af_inetcommon.c  (C)
  1. AF_xxx families β€” ni_af_inetcommon.c (C)

these are the code modules for AF families. currently supported: AF_INET, AF_INET6. there's partial support for AF_LINK and AF_PACKET so we can retrieve the MAC address from the interface where it's needed. and whenever the code is reasonably universal for a particular address family and for the methods used to pull the info off the OS, it lives in an af_xxxx.c file. iconic behavior ✨

  1. IFREQ families β€” ni_xx_ifreq.c (C)

code modules for IFREQ families. currently supported are:

  • ifreq β€” ni_ifreq.c

supports retrieval of ipV4 information. the ifreq structure just doesn't have enough space to return data about socket address families bigger than struct sockaddr. also, all known operating systems support this flavor of data retrieval, so it's the universal one. ni_ifreq.c makes use of calls to ni_af_inet.c.

  • in6_ifreq β€” ni_in6_ifreq.c

supports retrieval of both ipV4 and ipV6 information. in6_ifreq uses struct sockaddr_storage instead of the smaller struct sockaddr that ifreq uses. this code module supports variants of the BSD operating system and a few others. ni_in6_ifreq makes use of calls to ni_af_inetcommon.c.

  • lifreq β€” ni_lifreq.c

supports retrieval of both ipV4 and ipV6 information. lifreq has a custom format unique to the SUN operating systems. pretty much everything in it, even though it's similar to the two previous code modules, is custom. it's giving "we do our own thing here" πŸ’…

  • linuxproc β€” ni_linuxproc.c

supports retrieval of both ipV4 and ipV6 information. linuxproc uses calls to ni_af_inet.c to get ipV4 information into getifaddrs format, plus custom code to collect similarly formatted ipV6 information directly from the /proc file system. then it performs a merge on these two data sets to put them in proper order, and adds fake AF_LINK or AF_PACKET records so you get getifaddrs-compatible access to the MAC address through the returned struct ifaddrs array. obsessed πŸ“

  1. getifaddrs β€” ni_getifaddrs.c (C)

the getifaddrs code module holds the decision mechanism for how data gets retrieved for a particular build of Net::Interface. at build time, portions of the code are #ifdef'd in/out depending on the availability of resources from the underlying OS. and in addition, at run time, if the system doesn't have native getifaddrs, then a decision tree kicks in based on the response to calls for data to the various code modules described in section 2. it's giving "runtime detective work" πŸ”₯

  1. Sub-system β€” Interface.xs (PERLXS)

this file asks for the data about the interfaces with a generic call to getifaddrs. the data returned lives in memory allocated by the OS, and it must be freed or a memory leak will result, because it is not tracked by Perl's garbage collector. Interface.xs moves the interface data from allocated memory into Perl managed memory, where it can be reclaimed by the garbage collection mechanism if/when the user space program turns it loose. this eliminates the need for a close operation to free the OS's allocated memory. no leaks, no drama, no cap πŸ’―

  1. User space β€” Interface.pm (Perl)

the pure perl portion of this module performs most of the presentation operations for the user that are published in the API for Net::Interface.


🌈 DATA FLOW BLOCK DIAGRAM 🌈

here's the whole vibe in diagram form, bestie. it's giving ✨main character energy✨:

           *\  \  \    |    /  /  /*
           *      user space       *
           *************************
                       ^                                Net::Interface
                       |                           Architecture Block Diagram
                       v
           *************************
           *      Interface.pm     *
           *************************
                       |
           *************************
           *      Interface.xs     *
           *************************
                       |
           *************************               *************************
           *   system getifaddrs   *               *      ni_getifreqs     *
           *          via          *<-if missing ->*           via         *
           *   (ni_getifaddrer.c)  *               *    (ni_ifreq.c)       *
           *************************               *    (ni_lifreq.c)      *
                                                   *    (ni_in6_ifreq.c)   *
                                                   *    (ni_linuxproc.c)   *
                                                   *************************
                                                               |
                                                   *************************
                                                   *  (ni_af_inetcommon.c) *
                                                   *************************

πŸŽ€ DEVELOPER API πŸŽ€

access to the pieces of code in the block diagram above are available through a developer API. these code snippets from Interfaces.xs describe the access:

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);

and…..

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;

both function sets result in a printed description to the terminal window, so you can do code creation and debug. currently the ref is unused. it's expected that future development will modify or add to the function access. iykyk 🌸

# test.pl for developer
#
use strict;
use Net::Interface;

# to call OS native getifaddrs if present
print "\nifreq\n";  gifaddrs_base Net::Interface();

# to call ni_linuxproc fallback getifaddrs
print "\nlxp\n";    gifa_linuxproc Net::Interface();

# to call ni_linuxproc ifreq emulation
print "\nglxp\n";   d_ni_linuxproc Net::Interface();

See: test.pl.developer


πŸ’œ DEVELOPER API DESCRIPTION πŸ’œ

if you've gotten this far, bestie, it is time to read some of the code. AF_familes and IFREQ_families are accessed through constructor structs found at the bottom of each of the ni_af_xxx and ni_xx_ifreq source files. their vectoring components are described in ni_func.h near the bottom, and in ni_util.c in the section labeled constructor registration β€” the essence of which is described here:

struct ni_ifconf_flavor * ni_ifcf_get(enum ni_FLAVOR type)
struct ni_ifconf_flavor * ni_safe_ifcf_get(enum ni_FLAVOR type);

nifp = ni_ifcf_get(NI_IFREQ);

returns a pointer nifp to the structure for a particular flavor of ifreq. if a flavor is unsupported on a particular architecture, a NULL is returned by the first invocation and NI_IFREQ by the second. currently supported flavors are:

enum ni_FLAVOR {
      NI_NULL,        reserved for the getifaddrs base system call
      NI_IFREQ,
      NI_LIFREQ,
      NI_IN6_IFREQ,
      NI_LINUXPROC
};
struct ni_ifconf_flavor {
  enum ni_FLAVOR              ni_type;
  int                         (*gifaddrs)
  int                         siocgifindex;
  int                         siocsifaddr;
  int                         siocgifaddr;
  int                         siocdifaddr;
  int                         siocaifaddr;
  int                         siocsifdstaddr;
  int                         siocgifdstaddr;
  int                         siocsifflags;
  int                         siocgifflags;
  int                         siocsifmtu;
  int                         siocgifmtu;
  int                         siocsifbrdaddr;
  int                         siocgifbrdaddr;
  int                         siocsifnetmask;
  int                         siocgifnetmask;
  int                         siocsifmetric;
  int                         siocgifmetric;
  int                         ifr_offset;
  void                        (*fifaddrs)      howto free ifaddrs
  int                         (*refreshifr)    howto refresh ifreq
  void *                      (*getifreqs)     howto get ifreq
  int                         (*developer)     developer access
  struct ni_ifconf_flavor *   ni_ifcf_next;
};

🌟 MACROS 🌟

  • NI_PRINT_MAC(u_char * hex_mac_string);

a printf statement for terminal output of the form:

XX:XX:XX:XX:XX:XX:XX:XX
  • NI_MAC_NOT_ZERO(u_char * hex_mac_string)

like:

if( NI_MAC_NOT_ZERO(macp))
      do something
  • NI_PRINT_IPV6(struct sin6_addr);

takes an argument of the form sockaddr_in6.sin6_addr and prints:

XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX

✨ FUNCTIONS ✨

okay bestie, this is the part where it gets juicy β€” all the actual C functions this module hands you. buckle up πŸ’–


int ni_clos_reopn_dgrm(int fd, int af)

This one closes a socket and then immediately opens an ioctl socket of type SOCK_DGRAM, handing you back the socket value ✨.

Small but important plot twist: if that socket value comes back NEGATIVE, no close ever happened, and the call is literally just the same as doing:

socket(af,SOCK_DGRAM,0)

so yeah, she tried, she closed nothing, she's just a socket() call in a trench coat πŸ™ˆ

void ni_gifa_free(struct ifaddrs * ifap, int flavor)

Different getifaddrs flavors need different free functions, bestie β€” so you hand this girl the ifaddrs structure list and the flavor it came from, and she calls the appropriate free memory function for you. no mismatched free/alloc energy here, we do not do that πŸ’…

int nifreq_gifaddrs(struct ifaddrs **ifap, struct ni_ifconf_flavor *nifp)

This is our semi-standard version of getifaddrs, the one that gets used by OS's that hand you ifreq and in6_ifreq instead of the nice stuff 🌸

NOTE: all calls to getifaddrs return -1 on failure and the FLAVOR as enumerated above on success.

like, for real for real:

i.e. NI_NULL for the native getifaddrs, NI_IFREQ, NI_LINUXPROC, etc...

so check the flavor, that's your iykyk moment πŸ‘‘

uint32_t ni_ipv6addr_gettype(struct in6_addr * in6p)

She reaches into an ipV6 address and extracts info about what type of address it is πŸ¦‹. The value she hands back? You can pass it straight into the NEXT function call to print it. chain reaction, main character energy, we love a handoff ✨

int ni_lx_map2scope(int lscope)

This maps Linux style scope bits to their RFC-2373 equivalent. here's the whole vibe check:

    scope flags rfc-2373
        0       reserved
        1    node-local (aka loopback, interface-local)
        2    link-local
        3       unassigned
        4       unassigned
        5    site-local
        6       unassigned
        7       unassigned
        8    organization-local
        9       unassigned
        A       unassigned
        B       unassigned
        C       unassigned
        D       unassigned
        E    global scope
        F       reserved

      Linux   rfc-2372
     0x0000     0xe     GLOBAL
     0x0010u    0x1     NODELOCAL, LOOPBACK, INTERFACELOCAL
     0x0020u    0x2     LINKLOCAL
     0x0040u    0x5     SITELOCAL

void ni_linux_scope2txt(uint32_t type)

Print information about an ipV6 address for each bit present in type πŸ’–. and the table she's pulling from is iconic, look at this:

  const ni_iff_t ni_lx_type2txt[] = {
        { IPV6_ADDR_ANY,                "unknown" },
        { IPV6_ADDR_UNICAST,            "unicast" },
        { IPV6_ADDR_MULTICAST,          "multicast" },
        { IPV6_ADDR_ANYCAST,            "anycast" },
        { IPV6_ADDR_LOOPBACK,           "loopback" },
        { IPV6_ADDR_LINKLOCAL,          "link-local" },
        { IPV6_ADDR_SITELOCAL,          "site-local" },
        { IPV6_ADDR_COMPATv4,           "compat-v4" },
        { IPV6_ADDR_SCOPE_MASK,         "scope-mask" },
        { IPV6_ADDR_MAPPED,             "mapped" },
        { IPV6_ADDR_RESERVED,           "reserved" },
        { IPV6_ADDR_ULUA,               "uniq-lcl-unicast" },
        { IPV6_ADDR_6TO4,               "6to4" },
        { IPV6_ADDR_6BONE,              "6bone" },
        { IPV6_ADDR_AGU,                "global-unicast" },
        { IPV6_ADDR_UNSPECIFIED,        "unspecified" },
        { IPV6_ADDR_SOLICITED_NODE,     "solicited-node" },
        { IPV6_ADDR_ISATAP,             "ISATAP" },
        { IPV6_ADDR_PRODUCTIVE,         "productive" },
        { IPV6_ADDR_6TO4_MICROSOFT,     "6to4-ms" },
        { IPV6_ADDR_TEREDO,             "teredo" },
        { IPV6_ADDR_ORCHID,             "orchid" },
        { IPV6_ADDR_NON_ROUTE_DOC,      "non-routeable-doc" }
  };

no cap, she ate πŸ“

int ni_sizeof_type2txt()

Returns the size of the above table. tiny function, huge support role, the bestie who holds your bag while you slay 🫢

u_int ni_get_scopeid(struct sockaddr_in6 * sin6)

On systems using KAME, she extracts and returns the scope from field 2 of the ipV6 address, and sets fields 2,3 to zero 🧚. On all other systems she just returns:

    sin6->sin6_scopeid

and here's the scope flags table, because of course there's a table:

   scope flags     rfc-2373

        0         reserved
        1       node-local
        2       link-local
        3       unassigned
        4       unassigned
        5       site-local
        6       unassigned
        7       unassigned
        8       organization-local
        9       unassigned
        A         unassigned
        B         unassigned
        C         unassigned
        D         unassigned
        E       global scope
        F       reserved

void * ni_memdup(void *memp, int size)

Allocates memory for size and copies the contents from memp πŸ’œ. Returns NULL on error, and sets errno to ENOMEM. so if you get NULL back, that's the memory gods saying "not today" 😭

void ni_plen2mask(void * in_addr, int plen, int sizeofaddr)

Creates a NETMASK string from a prefix length ✨. usage:

For ipV4:

ni_plen2mask(&in_addr, cidr, sizeof(struct in_addr));

For ipV6:

ni_plen2mask(&in6_addr, cidr, sizeof(struct in6_addr));

int ni_prefix(void * ap, int len, int size)

Calculates the prefix length for a NETMASK, where *ap points to the binary representation of the NETMASK and size is the number of bytes in the mask πŸ¦‹. usage:

For ipV4:

ni_prefix(&in_addr,sizeof(struct in_addr));

For ipV6:

ni_prefix(&in6_addr,sizeof(struct(in6_addr));

(yes bestie, those are copied character for character β€” do not come for me, I am simply the messenger πŸ’…)

int ni_refresh_ifreq(int fd, struct ifconf *ifc, void **oifr, void **olifr, struct ni_ifconf_flavor * nifp)

So some OS's just lose scope on the particular device/addr handle when certain ioctl's get performed. rude honestly. This function refreshes the ifconf chain and repositions the pointers in the exact same spot, but now with fresh scope 🌈.

Wanna see it in the wild? See ni_in6_ifreq.c and ni_af_net6.c for usage β€” search for the string refreshifr. The code snippit looks like:

        nifp->refreshir

        Copyright 2008-2009 - Michael Robinton

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version πŸ’–

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 the GNU General Public License in the file named Copying for more details.

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.

and that's the whole page, bestie 🫢 this part two came from perl v5.42.3, dated 2026-08-19, and it lives under the Net::I...veloper(3pm) page in the User Contributed Perl Documentation ✨ go forth and refresh those ifreq chains, you absolute queen πŸ‘‘πŸ’–

πŸ“¦ where this page comes from