Kernel version: Linux Version 3.10.14
1. As each development board boot card physical address of eth0 are random
Then the line can be found on the Internet to achieve physical address set mac command:
ifconfig eth0 down
ifconfig eth0 hw ether 1234567890ab
ifconfig eth0 up
Then take curious to see how the kernel command line ifconfig is interacting with, I want to try how to automatically set the MAC directly by the kernel .
2. Analysis Introduction
Because the command is ifconfig, the code is busybox, but we find in the documentation directory of the kernel of the ifconfig introduction, introduce the code files are located in: documentation \ Networking \ ifenslave.c
2.1 shown below, and the corresponding ifconfig eth0 down ifconfig eth0 up function is:
For example, when we knock ifconfig eth0 down time, but in reality it is to call:
set_if_down("eth0", master_flags.ifr_flags);
In addition to the file on the map, there are used the following functions:
set_if_addr (); // Set the address (including IP, mask, broadcast, the destination) set_master_hwaddr (); // set the physical address mac
Next we eth0, for example, to track ifconfig up / down and ifconfig eth0 hw ether how to call the kernel
3. Analyze set_if_up () function
3.1 Analysis set_if_up ()
set_if_up () function will be called set_if_flags ( "eth0", flags | IFF_UP) , to add ifname (eth0) open flag
3.2 分析set_if_up()->set_if_flags("eth0", flags | IFF_UP)
The function is as follows:
static int set_if_flags(char *ifname, short flags) { struct ifreq ifr; int res = 0; ifr.ifr_flags = flags; strncpy (ifr.ifr_name, ifname, IFNAMSIZ); // ifr.ifr_name = "eth0" RES = the ioctl (skfd, SIOCSIFFLAGS, & ifr); // by ioctl () command is transmitted to the kernel variable SIOCSIFFLAGS and ifr Socket IF (RES < 0 ) { saved_errno = errno; v_print("Interface '%s': Error: SIOCSIFFLAGS failed: %s\n", ifname, strerror(saved_errno)); } else { v_print("Interface '%s': flags set to %04X.\n", ifname, flags); } return res; }
3.3 macro SIOCSIFFLAGS looking to see where in the kernel to achieve it
Locate the net \ core \ Dev_ioctl.c of dev_ioctl () function
The important part of the function code is as follows:
int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg) { struct ifreq ifr; int ret; char *colon; //… … switch (cmd) { //… … Case SIOCSIFFLAGS: // flag is set up such as the ifconfig / Down Case SIOCSIFMETRIC: Case SIOCSIFMTU: // Set MUT length Case SIOCSIFHWADDR: // set the physical address mac // ... ... dev_load (NET, ifr.ifr_name); // by ifr .ifr_name (eth0) name to load the card rtnl_lock (); // to net_device be locked, to avoid conflict with the operation of RET = dev_ifsioc (NET, & IFR, cmd); // ultimately call the function rtnl_unlock(); Return the right; // ... ... }
As can be seen from the above, we set the mac flow when the physical address is also running here, eventually they will call dev_ifsioc (net, & ifr, cmd ) function
4. The back is very simple, and ultimately ifconfig eth0 up call the kernel process:
set_if_up()-> set_if_flags("eth0", flags | IFF_UP)-> dev_ifsioc(net, &ifr, cmd)-> dev_change_flags(dev, ifr->ifr_flags)-> __dev_change_flags(dev, flags);
Then 4.1 __dev_change_flags (dev, flags) function, is determined by the value of the flag whether the bit IFF_UP contrary, calling achieved __dev_close () or __dev_open () to switch eth0
As shown below:
Member function in net_device_ops structure 4.2 and __dev_open () will call the NIC driver to achieve open
__dev_open(dev): dev -> netdev_ops-> ndo_validate_addr (dev); // test dev-> dev_addr (hw addr) is valid, usually calling eth_validate_addr () function, you need to pay attention to hw_addr [0] is the least significant bit is not 1 dev-> netdev_ops -> ndo_open (dev); // call open () function to achieve ifconfig up
4.3 Similarly __dev_close () member function calls the following achieve closure:
dev-> netdev_ops-> ndo_stop (dev); // call the stop () function to achieve ifconfig down
4.4 Where to find net_device_ops structure member functions located
Mentioned above is the struct net_device dev variable types, and struct net_device represent one of our network card driver equipment, registration documents are in the variable kernel drivers / net directory, to register by register_netdev () kernel function in the kernel.
We have our board dm9000 card, for example, the file is located in drivers / net / Ethernet / davicom / dm9000.c, then you can find it ndo_open ():
5. For ifconfig eth0 hw ether NIC setting process are as follows:
set_master_hwaddr(master_ifname,&(slave_hwaddr.ifr_hwaddr))-> ioctl (skfd, SIOCSIFHWADDR, &ifr) -> dev_ifsioc(net, &ifr, cmd)-> dev_set_mac_address (dev, & ifr-> ifr_hwaddr) -> // set the MAC address dev -> netdev_ops-> ndo_set_mac_address (dev, & ifr-> ifr_hwaddr); // final call net_device ops member functions to achieve the set
6. Implement kernel boot automatically set fixed MAC address
After the analysis process, then we have to implement it.
6.1 dm9000 card to our board as an example
We find register_netdev () location in the drivers / net / Ethernet / davicom / dm9000.c of dm9000_probe function in:
Then 6.2 register_netdev () function to add the code below:
struct the sockaddr HWaddr; // to store the MAC address structure rtnl_lock(); RET = dev_close (jz_ndev); // first need to close the card, in case rtnl_unlock (); hwaddr.sa_family = ndev->type; hwaddr.sa_data [ 0 ] = 0x12 ; // note, data [0] is the least significant bit is not 1, that is not the first odd hwaddr.sa_data [ 1 ] = 0x34 ; hwaddr.sa_data [ 2 ] = 0x56 ; hwaddr.sa_data [ 3 ] = 0x78 ; hwaddr.sa_data [ 4 ] = 0x90 ; hwaddr.sa_data [ 5 ] = 0xab ; rtnl_lock(); RET = dev_set_mac_address (jz_ndev, & HWaddr); // call the function to our analysis, to set the mac address rtnl_unlock ();
6.3 Compile - Test
After starting the input ifconfig , you can see the kernel has helped me set up: