| /* |
| * ipconfig/netdev.c |
| * |
| * ioctl-based device configuration |
| */ |
| #include <sys/types.h> |
| #include <sys/socket.h> |
| #include <errno.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <net/if.h> |
| #include <net/if_arp.h> |
| #include <netinet/in.h> |
| #include <linux/route.h> |
| |
| #include "netdev.h" |
| |
| static int cfd = -1; |
| |
| static void copy_name(struct netdev *dev, struct ifreq *ifr) |
| { |
| strncpy(ifr->ifr_name, dev->name, sizeof(ifr->ifr_name)); |
| ifr->ifr_name[sizeof(ifr->ifr_name) - 1] = '\0'; |
| } |
| |
| int netdev_getflags(struct netdev *dev, short *flags) |
| { |
| struct ifreq ifr; |
| |
| copy_name(dev, &ifr); |
| |
| if (ioctl(cfd, SIOCGIFFLAGS, &ifr) == -1) { |
| perror("SIOCGIFFLAGS"); |
| return -1; |
| } |
| |
| *flags = ifr.ifr_flags; |
| return 0; |
| } |
| |
| static int netdev_sif_addr(struct ifreq *ifr, int cmd, __u32 addr) |
| { |
| struct sockaddr_in *sin = (struct sockaddr_in *)&ifr->ifr_addr; |
| |
| sin->sin_family = AF_INET; |
| sin->sin_addr.s_addr = addr; |
| |
| return ioctl(cfd, cmd, ifr); |
| } |
| |
| int netdev_setaddress(struct netdev *dev) |
| { |
| struct ifreq ifr; |
| |
| copy_name(dev, &ifr); |
| |
| if (dev->ip_addr != INADDR_ANY && |
| netdev_sif_addr(&ifr, SIOCSIFADDR, dev->ip_addr) == -1) { |
| perror("SIOCSIFADDR"); |
| return -1; |
| } |
| |
| if (dev->ip_broadcast != INADDR_ANY && |
| netdev_sif_addr(&ifr, SIOCSIFBRDADDR, dev->ip_broadcast) == -1) { |
| perror("SIOCSIFBRDADDR"); |
| return -1; |
| } |
| |
| if (dev->ip_netmask != INADDR_ANY && |
| netdev_sif_addr(&ifr, SIOCSIFNETMASK, dev->ip_netmask) == -1) { |
| perror("SIOCSIFNETMASK"); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| int netdev_setdefaultroute(struct netdev *dev) |
| { |
| struct rtentry r; |
| |
| if (dev->ip_gateway == INADDR_ANY) |
| return 0; |
| |
| memset(&r, 0, sizeof(r)); |
| |
| ((struct sockaddr_in *)&r.rt_dst)->sin_family = AF_INET; |
| ((struct sockaddr_in *)&r.rt_dst)->sin_addr.s_addr = INADDR_ANY; |
| ((struct sockaddr_in *)&r.rt_gateway)->sin_family = AF_INET; |
| ((struct sockaddr_in *)&r.rt_gateway)->sin_addr.s_addr = dev->ip_gateway; |
| ((struct sockaddr_in *)&r.rt_genmask)->sin_family = AF_INET; |
| ((struct sockaddr_in *)&r.rt_genmask)->sin_addr.s_addr = INADDR_ANY; |
| r.rt_flags = RTF_UP | RTF_GATEWAY; |
| |
| if (ioctl(cfd, SIOCADDRT, &r) == -1 && errno != EEXIST) { |
| perror("SIOCADDRT"); |
| return -1; |
| } |
| return 0; |
| } |
| |
| int netdev_setmtu(struct netdev *dev) |
| { |
| struct ifreq ifr; |
| |
| copy_name(dev, &ifr); |
| ifr.ifr_mtu = dev->mtu; |
| |
| return ioctl(cfd, SIOCSIFMTU, &ifr); |
| } |
| |
| static int netdev_gif_addr(struct ifreq *ifr, int cmd, __u32 *ptr) |
| { |
| struct sockaddr_in *sin = (struct sockaddr_in *)&ifr->ifr_addr; |
| |
| if (ioctl(cfd, cmd, ifr) == -1) |
| return -1; |
| |
| *ptr = sin->sin_addr.s_addr; |
| |
| return 0; |
| } |
| |
| int netdev_up(struct netdev *dev) |
| { |
| struct ifreq ifr; |
| |
| copy_name(dev, &ifr); |
| |
| if (ioctl(cfd, SIOCGIFFLAGS, &ifr) == -1) { |
| perror("SIOCGIFFLAGS"); |
| return -1; |
| } |
| |
| ifr.ifr_flags |= IFF_UP; |
| |
| if (ioctl(cfd, SIOCSIFFLAGS, &ifr) == -1) { |
| perror("SIOCSIFFLAGS"); |
| return -1; |
| } |
| return 0; |
| } |
| |
| int netdev_down(struct netdev *dev) |
| { |
| struct ifreq ifr; |
| |
| copy_name(dev, &ifr); |
| |
| if (ioctl(cfd, SIOCGIFFLAGS, &ifr) == -1) { |
| perror("SIOCGIFFLAGS"); |
| return -1; |
| } |
| |
| ifr.ifr_flags &= ~IFF_UP; |
| |
| if (ioctl(cfd, SIOCSIFFLAGS, &ifr) == -1) { |
| perror("SIOCSIFFLAGS"); |
| return -1; |
| } |
| return 0; |
| } |
| |
| int netdev_init_if(struct netdev *dev) |
| { |
| struct ifreq ifr; |
| |
| if (cfd == -1) |
| cfd = socket(AF_INET, SOCK_DGRAM, 0); |
| if (cfd == -1) { |
| perror("socket(AF_INET)"); |
| return -1; |
| } |
| |
| copy_name(dev, &ifr); |
| |
| if (ioctl(cfd, SIOCGIFINDEX, &ifr) == -1) { |
| perror("SIOCGIFINDEX"); |
| return -1; |
| } |
| |
| dev->ifindex = ifr.ifr_ifindex; |
| |
| if (ioctl(cfd, SIOCGIFMTU, &ifr) == -1) { |
| perror("SIOCGIFMTU"); |
| return -1; |
| } |
| |
| dev->mtu = ifr.ifr_mtu; |
| |
| if (ioctl(cfd, SIOCGIFHWADDR, &ifr) == -1) { |
| perror("SIOCGIFHWADDR"); |
| return -1; |
| } |
| |
| dev->hwtype = ifr.ifr_hwaddr.sa_family; |
| dev->hwlen = 0; |
| |
| switch (dev->hwtype) { |
| case ARPHRD_ETHER: |
| dev->hwlen = 6; |
| break; |
| case ARPHRD_EUI64: |
| dev->hwlen = 8; |
| break; |
| case ARPHRD_LOOPBACK: |
| dev->hwlen = 0; |
| break; |
| default: |
| return -1; |
| } |
| |
| memcpy(dev->hwaddr, ifr.ifr_hwaddr.sa_data, dev->hwlen); |
| memset(dev->hwbrd, 0xff, dev->hwlen); |
| |
| /* |
| * Try to get the current interface information. |
| */ |
| if (dev->ip_addr == INADDR_NONE && |
| netdev_gif_addr(&ifr, SIOCGIFADDR, &dev->ip_addr) == -1) { |
| perror("SIOCGIFADDR"); |
| dev->ip_addr = 0; |
| dev->ip_broadcast = 0; |
| dev->ip_netmask = 0; |
| return 0; |
| } |
| |
| if (dev->ip_broadcast == INADDR_NONE && |
| netdev_gif_addr(&ifr, SIOCGIFBRDADDR, &dev->ip_broadcast) == -1) { |
| perror("SIOCGIFBRDADDR"); |
| dev->ip_broadcast = 0; |
| } |
| |
| if (dev->ip_netmask == INADDR_NONE && |
| netdev_gif_addr(&ifr, SIOCGIFNETMASK, &dev->ip_netmask) == -1) { |
| perror("SIOCGIFNETMASK"); |
| dev->ip_netmask = 0; |
| } |
| |
| return 0; |
| } |