---
# System prepended metadata

title: netdevice

---

netdevice
===

Linux 提供了一系列的低階(low-level)網路介面，可供開發者使用獲取網路裝置資訊。

我們可以通過syscall ioctl去取用這些資訊[ioctl 原型](https://man7.org/linux/man-pages/man2/ioctl.2.html):
```Clike=
#include <sys/ioctl.h>
int ioctl(int d, int request, ...);
```

可以送出的需求如下[sockios.h](https://github.com/torvalds/linux/blob/master/include/uapi/linux/sockios.h):

```Clike=
/* Routing table calls. */
#define SIOCADDRT	0x890B		/* add routing table entry	*/
#define SIOCDELRT	0x890C		/* delete routing table entry	*/
#define SIOCRTMSG	0x890D		/* unused			*/

/* Socket configuration controls. */
#define SIOCGIFNAME	0x8910		/* get iface name		*/
#define SIOCSIFLINK	0x8911		/* set iface channel		*/
#define SIOCGIFCONF	0x8912		/* get iface list		*/
#define SIOCGIFFLAGS	0x8913		/* get flags			*/
#define SIOCSIFFLAGS	0x8914		/* set flags			*/
#define SIOCGIFADDR	0x8915		/* get PA address		*/
#define SIOCSIFADDR	0x8916		/* set PA address		*/
#define SIOCGIFDSTADDR	0x8917		/* get remote PA address	*/
#define SIOCSIFDSTADDR	0x8918		/* set remote PA address	*/
#define SIOCGIFBRDADDR	0x8919		/* get broadcast PA address	*/
#define SIOCSIFBRDADDR	0x891a		/* set broadcast PA address	*/
#define SIOCGIFNETMASK	0x891b		/* get network PA mask		*/
#define SIOCSIFNETMASK	0x891c		/* set network PA mask		*/
#define SIOCGIFMETRIC	0x891d		/* get metric			*/
#define SIOCSIFMETRIC	0x891e		/* set metric			*/
#define SIOCGIFMEM	0x891f		/* get memory address (BSD)	*/
#define SIOCSIFMEM	0x8920		/* set memory address (BSD)	*/
#define SIOCGIFMTU	0x8921		/* get MTU size			*/
#define SIOCSIFMTU	0x8922		/* set MTU size			*/
#define SIOCSIFNAME	0x8923		/* set interface name */
#define	SIOCSIFHWADDR	0x8924		/* set hardware address 	*/
#define SIOCGIFENCAP	0x8925		/* get/set encapsulations       */
#define SIOCSIFENCAP	0x8926		
#define SIOCGIFHWADDR	0x8927		/* Get hardware address		*/
#define SIOCGIFSLAVE	0x8929		/* Driver slaving support	*/
#define SIOCSIFSLAVE	0x8930
#define SIOCADDMULTI	0x8931		/* Multicast address lists	*/
#define SIOCDELMULTI	0x8932
#define SIOCGIFINDEX	0x8933		/* name -> if_index mapping	*/
#define SIOGIFINDEX	SIOCGIFINDEX	/* misprint compatibility :-)	*/
#define SIOCSIFPFLAGS	0x8934		/* set/get extended flags set	*/
#define SIOCGIFPFLAGS	0x8935
#define SIOCDIFADDR	0x8936		/* delete PA address		*/
#define	SIOCSIFHWBROADCAST	0x8937	/* set hardware broadcast addr	*/
#define SIOCGIFCOUNT	0x8938		/* get number of devices */

#define SIOCGIFBR	0x8940		/* Bridging support		*/
#define SIOCSIFBR	0x8941		/* Set bridging options 	*/

#define SIOCGIFTXQLEN	0x8942		/* Get the tx queue length	*/
#define SIOCSIFTXQLEN	0x8943		/* Set the tx queue length 	*/

/* SIOCGIFDIVERT was:	0x8944		Frame diversion support */
/* SIOCSIFDIVERT was:	0x8945		Set frame diversion options */

#define SIOCETHTOOL	0x8946		/* Ethtool interface		*/

#define SIOCGMIIPHY	0x8947		/* Get address of MII PHY in use. */
#define SIOCGMIIREG	0x8948		/* Read MII PHY register.	*/
#define SIOCSMIIREG	0x8949		/* Write MII PHY register.	*/

#define SIOCWANDEV	0x894A		/* get/set netdev parameters	*/

#define SIOCOUTQNSD	0x894B		/* output queue size (not sent only) */
#define SIOCGSKNS	0x894C		/* get socket network namespace */

/* ARP cache control calls. */
		    /*  0x8950 - 0x8952  * obsolete calls, don't re-use */
#define SIOCDARP	0x8953		/* delete ARP table entry	*/
#define SIOCGARP	0x8954		/* get ARP table entry		*/
#define SIOCSARP	0x8955		/* set ARP table entry		*/

/* RARP cache control calls. */
#define SIOCDRARP	0x8960		/* delete RARP table entry	*/
#define SIOCGRARP	0x8961		/* get RARP table entry		*/
#define SIOCSRARP	0x8962		/* set RARP table entry		*/

/* Driver configuration calls */

#define SIOCGIFMAP	0x8970		/* Get device parameters	*/
#define SIOCSIFMAP	0x8971		/* Set device parameters	*/

/* DLCI configuration calls */

#define SIOCADDDLCI	0x8980		/* Create new DLCI device	*/
#define SIOCDELDLCI	0x8981		/* Delete DLCI device		*/

#define SIOCGIFVLAN	0x8982		/* 802.1Q VLAN support		*/
#define SIOCSIFVLAN	0x8983		/* Set 802.1Q VLAN options 	*/

/* bonding calls */

#define SIOCBONDENSLAVE	0x8990		/* enslave a device to the bond */
#define SIOCBONDRELEASE 0x8991		/* release a slave from the bond*/
#define SIOCBONDSETHWADDR      0x8992	/* set the hw addr of the bond  */
#define SIOCBONDSLAVEINFOQUERY 0x8993   /* rtn info about slave state   */
#define SIOCBONDINFOQUERY      0x8994	/* rtn info about bond state    */
#define SIOCBONDCHANGEACTIVE   0x8995   /* update to a new active slave */
			
/* bridge calls */
#define SIOCBRADDBR     0x89a0		/* create new bridge device     */
#define SIOCBRDELBR     0x89a1		/* remove bridge device         */
#define SIOCBRADDIF	0x89a2		/* add interface to bridge      */
#define SIOCBRDELIF	0x89a3		/* remove interface from bridge */

/* hardware time stamping: parameters in linux/net_tstamp.h */
#define SIOCSHWTSTAMP	0x89b0		/* set and get config		*/
#define SIOCGHWTSTAMP	0x89b1		/* get config			*/
```

---

我們可以開啟一個socket，並透過ioctl獲取網路介面資訊[Linux网络接口操作之ioctl-1_接口参数](https://segmentfault.com/a/1190000005138358)。如下範例，取得所有網路介面卡名稱[pi-router-os-core](https://github.com/sss22213/pi-router-os-core/blob/master/networkinfo/src/netinfo.c)。

```Clike=
/**
 * @brief Get all of interface name.
 * 
 * @return true 
 * @return false 
 */
bool netinfo_list_all_interface_name(char (*ary)[IFACE_NAME_LENGTH])
{
    int fd = 0;

    int ret = 0;

    // Create an structure of ifreq for save all of interface name.
    struct ifreq ifr[IFACE_MAXIMUN_LENGTH];

    // Create an structure of interface conf.
    struct ifconf ifc;
    
    // Alloc new memory space to save interface information.
    ifc.ifc_len = IFACE_MAXIMUN_LENGTH * sizeof(struct ifreq);
    ifc.ifc_buf = (char*)(ifr);

    // Create socket connection by IPV4, SOCK_DGRAM.
    fd = socket(AF_INET, SOCK_DGRAM, 0);

    // Send system call ioctl and argument SIOCGIFCONF.
    ret = ioctl(fd, SIOCGIFCONF, (char*)&ifc);

    close(fd);

    if (ret == -1) {
        return false;
    }

    // The loop will print all of interface name.
    for (int iface_idx = 0; iface_idx < ifc.ifc_len / sizeof(struct ifreq); iface_idx++) {
        strncpy(ary[iface_idx], ifr[iface_idx].ifr_name, sizeof(char)*IFACE_NAME_LENGTH);
    }

    return true;
}
```

:::info
[netdevice(7) linux manual](https://man7.org/linux/man-pages/man7/netdevice.7.html)
:::

如上範例，想要取回Interface name，必須透過兩個資料結構，從ioctl取回想要的資訊。

1. Interface conf structure [struct ifconf](https://github.com/torvalds/linux/blob/master/include/uapi/linux/if.h)。

```Clike=
/*
 * Structure used in SIOCGIFCONF request.
 * Used to retrieve interface configuration
 * for machine (useful for programs which
 * must know all networks accessible).
 */

/* for compatibility with glibc net/if.h */
struct ifconf  {
	int	ifc_len;			/* size of buffer	*/
	union {
		char __user *ifcu_buf;
		struct ifreq __user *ifcu_req;
	} ifc_ifcu;
};

#define	ifc_buf	ifc_ifcu.ifcu_buf		/* buffer address	*/
#define	ifc_req	ifc_ifcu.ifcu_req		/* array of structures	*/
```

:::info
這個structure只用於SIOCGIFCONF。也就是只負責取得Interface name and IPV4。
:::

2. 給予條件並獲取回傳資訊 [struct ifreq](https://github.com/torvalds/linux/blob/master/include/uapi/linux/if.h)。
```Clike=
/*
 * Interface request structure used for socket
 * ioctl's.  All interface ioctl's must have parameter
 * definitions which begin with ifr_name.  The
 * remainder may be interface specific.
 */
struct ifreq {
#define IFHWADDRLEN	6
	union
	{
		char	ifrn_name[IFNAMSIZ];		/* Interface name */
	} ifr_ifrn;
	
	union {
		struct	sockaddr ifru_addr;
		struct	sockaddr ifru_dstaddr;
		struct	sockaddr ifru_broadaddr;
		struct	sockaddr ifru_netmask;
		struct  sockaddr ifru_hwaddr;
		short	ifru_flags;
		int	ifru_ivalue;
		int	ifru_mtu;
		struct  ifmap ifru_map;
		char	ifru_slave[IFNAMSIZ];	/* Just fits the size */
		char	ifru_newname[IFNAMSIZ];
		void __user *	ifru_data;
		struct	if_settings ifru_settings;
	} ifr_ifru;
};
```

* 詳細來看struct ifreq分成兩個區塊。

a. 網路介面卡名稱interface name。
```Clike=
	union
	{
		char	ifrn_name[IFNAMSIZ];		/* Interface name */
	} ifr_ifrn;
```

b. 網路介面卡的其他資訊。
```Clike=
	union {
		struct	sockaddr ifru_addr;
		struct	sockaddr ifru_dstaddr;
		struct	sockaddr ifru_broadaddr;
		struct	sockaddr ifru_netmask;
		struct  sockaddr ifru_hwaddr;
		short	ifru_flags;
		int	ifru_ivalue;
		int	ifru_mtu;
		struct  ifmap ifru_map;
		char	ifru_slave[IFNAMSIZ];	/* Just fits the size */
		char	ifru_newname[IFNAMSIZ];
		void __user *	ifru_data;
		struct	if_settings ifru_settings;
	} ifr_ifru;
```

依據這個範例，我們可以從struct ifreq獲取interface name和ipv4，也就是ifrn_name和ifru_addr。

ifrn_name位於a部分，ifru_addr位於b部分。

觀察b部分，發現是由union修飾的結構，也就是說這些結構共用一塊相同的記憶體空間。依照不同的需求(例如: SIOCGIFHWADDR)，可以轉型成相對應的結構(ifr_hwaddr)獲取內容。

---

看到以下的範例，將想要查詢的interface放入ifrn_name，並送出ioctl取得netmask。[pi-router-os-core](https://github.com/sss22213/pi-router-os-core/blob/master/networkinfo/src/netinfo.c)

```Clike=
// Get MAC address
struct ifreq ifr;
strncpy(ifr.ifrn_name, "eth0", sizeof(char)*IFNAMSIZ);

ret = ioctl(fd, SIOCGIFHWADDR, &ifr);
if (ret == -1) {
    close(fd);
    return false;
    }

for (int mac_addr_idx = 0; mac_addr_idx < 6 ; mac_addr_idx++) {
    sprintf(macaddress + 3 * mac_addr_idx, "%02x:", ifr.ifr_hwaddr.sa_data[mac_addr_idx]);
}
```

將共用記憶體轉成目標結構ifr_hwaddr。
```Clike=
ifr.ifr_hwaddr.sa_data[mac_addr_idx]
```