in net/netdev/netdev_ioctl.c [776:1255]
static int netdev_ifr_ioctl(FAR struct socket *psock, int cmd,
FAR struct ifreq *req)
{
FAR struct net_driver_s *dev = NULL;
unsigned int idx = 0;
int ret = OK;
ninfo("cmd: %d\n", cmd);
net_lock();
/* Execute commands that do not need ifr_name or lifr_name */
switch (cmd)
{
case SIOCGIFCOUNT: /* Get number of devices */
req->ifr_count = netdev_count();
break;
#ifdef CONFIG_NET_IPv4
case SIOCGIFCONF: /* Return an interface list (IPv4) */
ret = netdev_ipv4_ifconf((FAR struct ifconf *)req);
break;
#endif
#ifdef CONFIG_NET_IPv6
case SIOCGLIFCONF: /* Return an interface list (IPv6) */
ret = netdev_ipv6_ifconf((FAR struct lifconf *)req);
break;
#endif
#ifdef CONFIG_NETDEV_IFINDEX
case SIOCSIFNAME: /* Set interface name */
{
FAR struct net_driver_s *tmpdev;
tmpdev = netdev_findbyindex(req->ifr_ifindex);
if (tmpdev != NULL)
{
strlcpy(tmpdev->d_ifname, req->ifr_name, IFNAMSIZ);
}
else
{
ret = -ENODEV;
}
}
break;
case SIOCGIFNAME: /* Get interface name */
{
FAR struct net_driver_s *tmpdev;
tmpdev = netdev_findbyindex(req->ifr_ifindex);
if (tmpdev != NULL)
{
strlcpy(req->ifr_name, tmpdev->d_ifname, IFNAMSIZ);
}
else
{
ret = -ENODEV;
}
}
break;
#endif
default:
if (req == NULL)
{
net_unlock();
return -ENOTTY;
}
if (net_ioctl_ifreq_arglen(psock->s_domain, cmd)
>= (ssize_t)sizeof(struct ifreq))
{
idx = netdev_ifr_split_idx(req);
UNUSED(idx);
dev = netdev_findbyname(req->ifr_name);
}
else if (net_ioctl_ifreq_arglen(psock->s_domain, cmd)
== (ssize_t)sizeof(struct in6_ifreq))
{
FAR struct in6_ifreq *ifr6 = (FAR struct in6_ifreq *)req;
dev = netdev_findbyindex(ifr6->ifr6_ifindex);
}
if (dev == NULL)
{
ret = -ENOTTY;
}
break;
}
if (dev == NULL)
{
net_unlock();
return ret;
}
/* Execute commands that need ifr_name or lifr_name */
switch (cmd)
{
#ifdef CONFIG_NET_IPv4
case SIOCGIFADDR: /* Get IP address */
ioctl_get_ipv4addr(&req->ifr_addr, dev->d_ipaddr);
break;
case SIOCGIFDSTADDR: /* Get P-to-P address */
ioctl_get_ipv4addr(&req->ifr_dstaddr, dev->d_draddr);
break;
case SIOCSIFDSTADDR: /* Set P-to-P address */
ioctl_set_ipv4addr(&dev->d_draddr, &req->ifr_dstaddr);
break;
case SIOCGIFBRDADDR: /* Get broadcast IP address */
ioctl_get_ipv4broadcast(&req->ifr_broadaddr, dev->d_ipaddr,
dev->d_netmask);
break;
case SIOCSIFBRDADDR: /* Set broadcast IP address */
ret = -ENOSYS;
break;
case SIOCGIFNETMASK: /* Get network mask */
ioctl_get_ipv4addr(&req->ifr_addr, dev->d_netmask);
break;
case SIOCSIFNETMASK: /* Set network mask */
ioctl_set_ipv4addr(&dev->d_netmask, &req->ifr_addr);
break;
#endif
#ifdef CONFIG_NET_IPv6
case SIOCGLIFADDR: /* Get IP address */
{
FAR struct lifreq *lreq = (FAR struct lifreq *)req;
idx = MIN(idx, CONFIG_NETDEV_MAX_IPv6_ADDR - 1);
ioctl_get_ipv6addr(&lreq->lifr_addr, dev->d_ipv6[idx].addr);
}
break;
case SIOCSLIFADDR: /* Set IP address */
{
FAR struct lifreq *lreq = (FAR struct lifreq *)req;
idx = MIN(idx, CONFIG_NETDEV_MAX_IPv6_ADDR - 1);
netdev_ipv6_removemcastmac(dev, dev->d_ipv6[idx].addr);
ioctl_set_ipv6addr(dev->d_ipv6[idx].addr, &lreq->lifr_addr);
netdev_ipv6_addmcastmac(dev, dev->d_ipv6[idx].addr);
netlink_device_notify_ipaddr(dev, RTM_NEWADDR, AF_INET6,
dev->d_ipv6[idx].addr, net_ipv6_mask2pref(dev->d_ipv6[idx].mask));
}
break;
case SIOCGLIFDSTADDR: /* Get P-to-P address */
{
FAR struct lifreq *lreq = (FAR struct lifreq *)req;
ioctl_get_ipv6addr(&lreq->lifr_dstaddr, dev->d_ipv6draddr);
}
break;
case SIOCSLIFDSTADDR: /* Set P-to-P address */
{
FAR struct lifreq *lreq = (FAR struct lifreq *)req;
ioctl_set_ipv6addr(dev->d_ipv6draddr, &lreq->lifr_dstaddr);
}
break;
case SIOCGLIFBRDADDR: /* Get broadcast IP address */
case SIOCSLIFBRDADDR: /* Set broadcast IP address */
ret = -ENOSYS;
break;
case SIOCGLIFNETMASK: /* Get network mask */
{
FAR struct lifreq *lreq = (FAR struct lifreq *)req;
idx = MIN(idx, CONFIG_NETDEV_MAX_IPv6_ADDR - 1);
ioctl_get_ipv6addr(&lreq->lifr_addr, dev->d_ipv6[idx].mask);
}
break;
case SIOCSLIFNETMASK: /* Set network mask */
{
FAR struct lifreq *lreq = (FAR struct lifreq *)req;
idx = MIN(idx, CONFIG_NETDEV_MAX_IPv6_ADDR - 1);
ioctl_set_ipv6addr(dev->d_ipv6[idx].mask, &lreq->lifr_addr);
}
break;
#endif
case SIOCGLIFMTU: /* Get MTU size */
case SIOCGIFMTU: /* Get MTU size */
req->ifr_mtu = NETDEV_PKTSIZE(dev) - dev->d_llhdrlen;
break;
case SIOCSIFMTU: /* Set MTU size */
NETDEV_PKTSIZE(dev) = req->ifr_mtu + dev->d_llhdrlen;
break;
#ifdef CONFIG_NET_ICMPv6_AUTOCONF
case SIOCIFAUTOCONF: /* Perform ICMPv6 auto-configuration */
ret = icmpv6_autoconfig(dev);
break;
#endif
case SIOCSIFFLAGS: /* Sets the interface flags */
/* Is this a request to bring the interface up? */
if ((req->ifr_flags & IFF_UP) != 0)
{
/* Yes.. bring the interface up */
ret = netdev_ifup(dev);
#ifdef CONFIG_NET_ARP_ACD
/* having address then start acd */
arp_acd_setup(dev);
#endif /* CONFIG_NET_ARP_ACD */
}
else
{
/* Yes.. take the interface down */
ret = netdev_ifdown(dev);
}
break;
case SIOCGIFFLAGS: /* Gets the interface flags */
req->ifr_flags = dev->d_flags;
break;
/* MAC address operations only make sense if Ethernet or 6LoWPAN are
* supported.
*/
#if defined(CONFIG_NET_ETHERNET) || defined(CONFIG_NET_6LOWPAN)
case SIOCGIFHWADDR: /* Get hardware address */
#ifdef CONFIG_NET_ETHERNET
if (dev->d_lltype == NET_LL_ETHERNET ||
dev->d_lltype == NET_LL_IEEE80211)
{
req->ifr_hwaddr.sa_family = ARPHRD_ETHER;
memcpy(req->ifr_hwaddr.sa_data,
dev->d_mac.ether.ether_addr_octet, IFHWADDRLEN);
}
else
#endif
#ifdef CONFIG_NET_6LOWPAN
if (dev->d_lltype == NET_LL_IEEE802154 ||
dev->d_lltype == NET_LL_PKTRADIO)
{
req->ifr_hwaddr.sa_family = ARPHRD_IEEE802154;
memcpy(req->ifr_hwaddr.sa_data,
dev->d_mac.radio.nv_addr,
dev->d_mac.radio.nv_addrlen);
}
else
#endif
{
nwarn("WARNING: Unsupported link layer\n");
ret = -EAFNOSUPPORT;
}
break;
case SIOCSIFHWADDR: /* Set hardware address -- will not take effect until ifup */
#ifdef CONFIG_NET_ETHERNET
if (dev->d_lltype == NET_LL_ETHERNET ||
dev->d_lltype == NET_LL_IEEE80211)
{
memcpy(dev->d_mac.ether.ether_addr_octet,
req->ifr_hwaddr.sa_data, IFHWADDRLEN);
}
else
#endif
#ifdef CONFIG_NET_6LOWPAN
if (dev->d_lltype == NET_LL_IEEE802154 ||
dev->d_lltype == NET_LL_PKTRADIO)
{
FAR struct radio_driver_s *radio;
struct radiodev_properties_s properties;
/* Get the radio properties */
radio = (FAR struct radio_driver_s *)dev;
DEBUGASSERT(radio->r_properties != NULL);
ret = radio->r_properties(radio, &properties);
if (ret >= 0)
{
dev->d_mac.radio.nv_addrlen = properties.sp_addrlen;
DEBUGASSERT(dev->d_mac.radio.nv_addrlen <=
sizeof(dev->d_mac.radio.nv_addr));
DEBUGASSERT(dev->d_mac.radio.nv_addrlen <=
sizeof(req->ifr_hwaddr.sa_data));
memcpy(dev->d_mac.radio.nv_addr,
req->ifr_hwaddr.sa_data, dev->d_mac.radio.nv_addrlen);
}
}
else
#endif
{
nerr("Unsupported link layer\n");
ret = -EAFNOSUPPORT;
}
break;
#endif
case SIOCSIFADDR: /* Set IP address */
#ifdef CONFIG_NET_IPv4
if (psock->s_domain != PF_INET6)
{
if (net_ipv4addr_cmp(dev->d_ipaddr,
((FAR struct sockaddr_in *)&req->ifr_addr)->sin_addr.s_addr))
{
break;
}
ioctl_set_ipv4addr(&dev->d_ipaddr, &req->ifr_addr);
netlink_device_notify_ipaddr(dev, RTM_NEWADDR, AF_INET,
&dev->d_ipaddr, net_ipv4_mask2pref(dev->d_netmask));
#ifdef CONFIG_NET_ARP_ACD
arp_acd_set_addr(dev);
#endif /* CONFIG_NET_ARP_ACD */
}
#endif
#ifdef CONFIG_NET_IPv6
if (psock->s_domain == PF_INET6)
{
FAR struct in6_ifreq *ifr6 = (FAR struct in6_ifreq *)req;
ret = netdev_ipv6_add(dev, ifr6->ifr6_addr.in6_u.u6_addr16,
ifr6->ifr6_prefixlen);
if (ret == OK)
{
netlink_device_notify_ipaddr(dev, RTM_NEWADDR, AF_INET6,
ifr6->ifr6_addr.in6_u.u6_addr16, ifr6->ifr6_prefixlen);
}
}
#endif
break;
case SIOCDIFADDR: /* Delete IP address */
#ifdef CONFIG_NET_IPv4
if (psock->s_domain != PF_INET6)
{
netlink_device_notify_ipaddr(dev, RTM_DELADDR, AF_INET,
&dev->d_ipaddr, net_ipv4_mask2pref(dev->d_netmask));
dev->d_ipaddr = 0;
}
#endif
#ifdef CONFIG_NET_IPv6
if (psock->s_domain == PF_INET6)
{
FAR struct in6_ifreq *ifr6 = (FAR struct in6_ifreq *)req;
ret = netdev_ipv6_del(dev, ifr6->ifr6_addr.in6_u.u6_addr16,
ifr6->ifr6_prefixlen);
if (ret == OK)
{
netlink_device_notify_ipaddr(dev, RTM_DELADDR, AF_INET6,
ifr6->ifr6_addr.in6_u.u6_addr16, ifr6->ifr6_prefixlen);
}
}
#endif
break;
#if defined(CONFIG_NETDEV_IOCTL) && defined(CONFIG_NETDEV_PHY_IOCTL)
#ifdef CONFIG_ARCH_PHY_INTERRUPT
case SIOCMIINOTIFY: /* Set up for PHY event notifications */
if (dev->d_ioctl)
{
FAR struct mii_ioctl_notify_s *notify =
&req->ifr_ifru.ifru_mii_notify;
ret = dev->d_ioctl(dev, cmd, (unsigned long)(uintptr_t)notify);
}
else
{
ret = -ENOSYS;
}
break;
#endif
case SIOCGMIIPHY: /* Get address of MII PHY in use */
case SIOCGMIIREG: /* Get MII register via MDIO */
case SIOCSMIIREG: /* Set MII register via MDIO */
if (dev->d_ioctl)
{
FAR struct mii_ioctl_data_s *mii_data =
&req->ifr_ifru.ifru_mii_data;
ret = dev->d_ioctl(dev, cmd,
(unsigned long)(uintptr_t)mii_data);
}
else
{
ret = -ENOSYS;
}
break;
#endif
#if defined(CONFIG_NETDEV_IOCTL) && defined(CONFIG_NETDEV_CAN_BITRATE_IOCTL)
case SIOCSCANBITRATE: /* Set bitrate of a CAN controller */
if (dev->d_flags & IFF_UP)
{
/* Cannot set bitrate if the interface is up. */
ret = -EBUSY;
break;
}
/* If down, fall-through to common code in SIOCGCANBITRATE. */
case SIOCGCANBITRATE: /* Get bitrate from a CAN controller */
if (dev->d_ioctl)
{
FAR struct can_ioctl_data_s *can_bitrate_data =
&req->ifr_ifru.ifru_can_data;
ret = dev->d_ioctl(dev, cmd,
(unsigned long)(uintptr_t)can_bitrate_data);
}
else
{
ret = -ENOSYS;
}
break;
#endif
#if defined(CONFIG_NETDEV_IOCTL) && defined(CONFIG_NETDEV_CAN_FILTER_IOCTL)
case SIOCACANEXTFILTER: /* Add an extended-ID filter */
case SIOCDCANEXTFILTER: /* Delete an extended-ID filter */
case SIOCACANSTDFILTER: /* Add a standard-ID filter */
case SIOCDCANSTDFILTER: /* Delete a standard-ID filter */
case SIOCCANRECOVERY: /* Recovery can controller when bus-off */
if (dev->d_ioctl)
{
FAR struct can_ioctl_filter_s *can_filter =
&req->ifr_ifru.ifru_can_filter;
ret = dev->d_ioctl(dev, cmd,
(unsigned long)(uintptr_t)can_filter);
}
else
{
ret = -ENOSYS;
}
break;
#endif
#if defined(CONFIG_NETDEV_IOCTL) && defined(CONFIG_NETDEV_CAN_STATE_IOCTL)
case SIOCGCANSTATE: /* Get state from a CAN/LIN controller */
case SIOCSCANSTATE: /* Set the LIN/CAN controller state */
if (dev->d_ioctl)
{
FAR struct can_ioctl_state_s *can_state =
&req->ifr_ifru.ifru_can_state;
ret = dev->d_ioctl(dev, cmd,
(unsigned long)(uintptr_t)can_state);
}
else
{
ret = -ENOSYS;
}
break;
#endif
#ifdef CONFIG_NETDEV_IFINDEX
case SIOCGIFINDEX: /* Index to name mapping */
req->ifr_ifindex = dev->d_ifindex;
break;
#endif
default:
ret = -ENOTTY;
break;
}
net_unlock();
return ret;
}