mirror of
https://github.com/apache/nuttx.git
synced 2025-01-13 07:28:38 +08:00
net/netdev: Support managing multiple IPv6 addresses by ioctl
1. Supporting `SIOCSIFADDR` and `SIOCDIFADDR` with Linux in6_ifreq struct to manage ipv6 addresses. Ref: https://man7.org/linux/man-pages/man7/netdevice.7.html 2. Supporting alias like 'eth0:0' for multiple IPv6 addresses, to keep previous ioctl `SIOCGLIFADDR`, `SIOCSLIFADDR`, `SIOCGLIFNETMASK` and `SIOCSLIFNETMASK` working. Ref: https://man7.org/linux/man-pages/man8/ifconfig.8.html Signed-off-by: Zhe Weng <wengzhe@xiaomi.com>
This commit is contained in:
parent
7f421a46ca
commit
3e4d847f42
6 changed files with 178 additions and 70 deletions
|
@ -1057,7 +1057,7 @@ static int net_rpmsg_drv_ioctl(FAR struct net_driver_s *dev, int cmd,
|
|||
ssize_t len;
|
||||
int ret;
|
||||
|
||||
len = net_ioctl_arglen(cmd);
|
||||
len = net_ioctl_arglen(PF_RPMSG, cmd);
|
||||
if (len >= 0)
|
||||
{
|
||||
FAR struct net_rpmsg_ioctl_s *msg;
|
||||
|
|
|
@ -335,6 +335,13 @@ struct in6_pktinfo
|
|||
int ipi6_ifindex; /* send/recv interface index */
|
||||
};
|
||||
|
||||
struct in6_ifreq
|
||||
{
|
||||
struct in6_addr ifr6_addr; /* The IPv6 address of the request */
|
||||
uint32_t ifr6_prefixlen; /* The IPv6 prefix length */
|
||||
int ifr6_ifindex; /* The interface index of the request */
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Public Data
|
||||
****************************************************************************/
|
||||
|
|
|
@ -301,7 +301,7 @@ void net_initialize(void);
|
|||
* Calculate the ioctl argument buffer length.
|
||||
*
|
||||
* Input Parameters:
|
||||
*
|
||||
* domain The socket domain
|
||||
* cmd The ioctl command
|
||||
*
|
||||
* Returned Value:
|
||||
|
@ -309,7 +309,7 @@ void net_initialize(void);
|
|||
*
|
||||
****************************************************************************/
|
||||
|
||||
ssize_t net_ioctl_arglen(int cmd);
|
||||
ssize_t net_ioctl_arglen(uint8_t domain, int cmd);
|
||||
|
||||
/****************************************************************************
|
||||
* Critical section management.
|
||||
|
|
|
@ -26,7 +26,9 @@
|
|||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <debug.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <net/if.h>
|
||||
#include <nuttx/net/netdev.h>
|
||||
|
@ -139,6 +141,77 @@ static int ifconf_ipv4_callback(FAR struct net_driver_s *dev, FAR void *arg)
|
|||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ifconf_ipv6_addr_callback
|
||||
*
|
||||
* Description:
|
||||
* Callback from netdev_ipv6_foreach() that does the real implementation of
|
||||
* netdev_ipv6_ifconf().
|
||||
*
|
||||
* Input Parameters:
|
||||
* dev - The network device for this callback.
|
||||
* addr - The IPv6 address.
|
||||
* arg - User callback argument
|
||||
*
|
||||
* Returned Value:
|
||||
* Zero is returned on success; a negated errno value is returned on any
|
||||
* failure.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_NET_IPv6
|
||||
static int ifconf_ipv6_addr_callback(FAR struct net_driver_s *dev,
|
||||
FAR struct netdev_ifaddr6_s *addr,
|
||||
FAR void *arg)
|
||||
{
|
||||
FAR struct ifconf_ipv6_info_s *info = (FAR struct ifconf_ipv6_info_s *)arg;
|
||||
FAR struct lifconf *lifc = info->lifc;
|
||||
|
||||
if (lifc->lifc_len + sizeof(struct lifreq) <= info->bufsize)
|
||||
{
|
||||
FAR struct lifreq *req =
|
||||
(FAR struct lifreq *)&lifc->lifc_buf[lifc->lifc_len];
|
||||
FAR struct sockaddr_in6 *inaddr =
|
||||
(FAR struct sockaddr_in6 *)&req->lifr_addr;
|
||||
#ifdef CONFIG_NETDEV_MULTIPLE_IPv6
|
||||
int addr_idx = addr - dev->d_ipv6;
|
||||
|
||||
/* There is space for information about another adapter. Within
|
||||
* each ifreq structure, lifr_name will receive the interface
|
||||
* name and lifr_addr the address. The actual number of bytes
|
||||
* transferred is returned in lifc_len.
|
||||
*/
|
||||
|
||||
if (addr_idx > 0)
|
||||
{
|
||||
/* eth0:0 represents the second addr on eth0 */
|
||||
|
||||
if (snprintf(req->lifr_name, IFNAMSIZ,
|
||||
"%s:%d", dev->d_ifname, addr_idx - 1) >= IFNAMSIZ)
|
||||
{
|
||||
nwarn("WARNING: ifname too long to print %s:%d\n",
|
||||
dev->d_ifname, addr_idx - 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
strlcpy(req->lifr_name, dev->d_ifname, IFNAMSIZ);
|
||||
}
|
||||
|
||||
inaddr->sin6_family = AF_INET6;
|
||||
inaddr->sin6_port = 0;
|
||||
net_ipv6addr_copy(inaddr->sin6_addr.s6_addr16, addr->addr);
|
||||
}
|
||||
|
||||
/* Increment the size of the buffer in any event */
|
||||
|
||||
lifc->lifc_len += sizeof(struct lifreq);
|
||||
|
||||
return OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ifconf_ipv6_callback
|
||||
*
|
||||
|
@ -160,10 +233,8 @@ static int ifconf_ipv4_callback(FAR struct net_driver_s *dev, FAR void *arg)
|
|||
static int ifconf_ipv6_callback(FAR struct net_driver_s *dev, FAR void *arg)
|
||||
{
|
||||
FAR struct ifconf_ipv6_info_s *info = (FAR struct ifconf_ipv6_info_s *)arg;
|
||||
FAR struct lifconf *lifc;
|
||||
|
||||
DEBUGASSERT(dev != NULL && info != NULL && info->lifc != NULL);
|
||||
lifc = info->lifc;
|
||||
|
||||
/* Check if this adapter has an IPv6 address assigned and is in the UP
|
||||
* state.
|
||||
|
@ -186,29 +257,7 @@ static int ifconf_ipv6_callback(FAR struct net_driver_s *dev, FAR void *arg)
|
|||
* cases.
|
||||
*/
|
||||
|
||||
if (lifc->lifc_len + sizeof(struct lifreq) <= info->bufsize)
|
||||
{
|
||||
FAR struct lifreq *req =
|
||||
(FAR struct lifreq *)&lifc->lifc_buf[lifc->lifc_len];
|
||||
FAR struct sockaddr_in6 *inaddr =
|
||||
(FAR struct sockaddr_in6 *)&req->lifr_addr;
|
||||
|
||||
/* There is space for information about another adapter. Within
|
||||
* each ifreq structure, lifr_name will receive the interface
|
||||
* name and lifr_addr the address. The actual number of bytes
|
||||
* transferred is returned in lifc_len.
|
||||
*/
|
||||
|
||||
strlcpy(req->lifr_name, dev->d_ifname, IFNAMSIZ);
|
||||
|
||||
inaddr->sin6_family = AF_INET6;
|
||||
inaddr->sin6_port = 0;
|
||||
net_ipv6addr_copy(inaddr->sin6_addr.s6_addr16, dev->d_ipv6addr);
|
||||
}
|
||||
|
||||
/* Increment the size of the buffer in any event */
|
||||
|
||||
lifc->lifc_len += sizeof(struct lifreq);
|
||||
return netdev_ipv6_foreach(dev, ifconf_ipv6_addr_callback, arg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -652,31 +652,32 @@ static int netdev_wifr_ioctl(FAR struct socket *psock, int cmd,
|
|||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: netdev_ifr_dev
|
||||
* Name: netdev_ifr_split_idx
|
||||
*
|
||||
* Description:
|
||||
* Verify the struct ifreq and get the Ethernet device.
|
||||
* Split the address index from device name like 'eth0:0'.
|
||||
*
|
||||
* Input Parameters:
|
||||
* req - The argument of the ioctl cmd
|
||||
*
|
||||
* Returned Value:
|
||||
* A pointer to the driver structure on success; NULL on failure.
|
||||
* The address index from device name.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static FAR struct net_driver_s *netdev_ifr_dev(FAR struct ifreq *req)
|
||||
static unsigned int netdev_ifr_split_idx(FAR struct ifreq *req)
|
||||
{
|
||||
if (req != NULL)
|
||||
{
|
||||
/* Find the network device associated with the device name
|
||||
* in the request data.
|
||||
*/
|
||||
FAR char *colon = strchr(req->ifr_name, ':');
|
||||
int idx;
|
||||
|
||||
return netdev_findbyname(req->ifr_name);
|
||||
if (colon)
|
||||
{
|
||||
*colon++ = '\0'; /* Remove suffix from device name */
|
||||
idx = atoi(colon);
|
||||
return idx >= 0 ? idx + 1 : 0; /* eth0:0 represents the second addr */
|
||||
}
|
||||
|
||||
return NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
|
@ -686,7 +687,7 @@ static FAR struct net_driver_s *netdev_ifr_dev(FAR struct ifreq *req)
|
|||
* Calculate the ioctl argument buffer length of ifreq.
|
||||
*
|
||||
* Input Parameters:
|
||||
*
|
||||
* domain The socket domain
|
||||
* cmd The ioctl command
|
||||
*
|
||||
* Returned Value:
|
||||
|
@ -694,12 +695,11 @@ static FAR struct net_driver_s *netdev_ifr_dev(FAR struct ifreq *req)
|
|||
*
|
||||
****************************************************************************/
|
||||
|
||||
static ssize_t net_ioctl_ifreq_arglen(int cmd)
|
||||
static ssize_t net_ioctl_ifreq_arglen(uint8_t domain, int cmd)
|
||||
{
|
||||
switch (cmd)
|
||||
{
|
||||
case SIOCGIFADDR:
|
||||
case SIOCSIFADDR:
|
||||
case SIOCGIFDSTADDR:
|
||||
case SIOCSIFDSTADDR:
|
||||
case SIOCGIFBRDADDR:
|
||||
|
@ -710,7 +710,6 @@ static ssize_t net_ioctl_ifreq_arglen(int cmd)
|
|||
case SIOCGIFMTU:
|
||||
case SIOCGIFHWADDR:
|
||||
case SIOCSIFHWADDR:
|
||||
case SIOCDIFADDR:
|
||||
case SIOCGIFCOUNT:
|
||||
case SIOCSIFFLAGS:
|
||||
case SIOCGIFFLAGS:
|
||||
|
@ -730,6 +729,11 @@ static ssize_t net_ioctl_ifreq_arglen(int cmd)
|
|||
case SIOCGIFINDEX:
|
||||
return sizeof(struct ifreq);
|
||||
|
||||
case SIOCSIFADDR:
|
||||
case SIOCDIFADDR:
|
||||
return domain == PF_INET6 ?
|
||||
sizeof(struct in6_ifreq) : sizeof(struct ifreq);
|
||||
|
||||
case SIOCGLIFADDR:
|
||||
case SIOCSLIFADDR:
|
||||
case SIOCGLIFDSTADDR:
|
||||
|
@ -769,6 +773,7 @@ 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);
|
||||
|
@ -827,15 +832,27 @@ static int netdev_ifr_ioctl(FAR struct socket *psock, int cmd,
|
|||
break;
|
||||
#endif
|
||||
default:
|
||||
if (net_ioctl_ifreq_arglen(cmd) > 0)
|
||||
if (req == NULL)
|
||||
{
|
||||
dev = netdev_ifr_dev(req);
|
||||
if (dev == NULL)
|
||||
{
|
||||
ret = -ENODEV;
|
||||
}
|
||||
net_unlock();
|
||||
return -ENOTTY;
|
||||
}
|
||||
else
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -857,12 +874,6 @@ static int netdev_ifr_ioctl(FAR struct socket *psock, int cmd,
|
|||
ioctl_get_ipv4addr(&req->ifr_addr, dev->d_ipaddr);
|
||||
break;
|
||||
|
||||
case SIOCSIFADDR: /* Set IP address */
|
||||
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));
|
||||
break;
|
||||
|
||||
case SIOCGIFDSTADDR: /* Get P-to-P address */
|
||||
ioctl_get_ipv4addr(&req->ifr_dstaddr, dev->d_draddr);
|
||||
break;
|
||||
|
@ -893,16 +904,18 @@ static int netdev_ifr_ioctl(FAR struct socket *psock, int cmd,
|
|||
case SIOCGLIFADDR: /* Get IP address */
|
||||
{
|
||||
FAR struct lifreq *lreq = (FAR struct lifreq *)req;
|
||||
ioctl_get_ipv6addr(&lreq->lifr_addr, dev->d_ipv6addr);
|
||||
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;
|
||||
ioctl_set_ipv6addr(dev->d_ipv6addr, &lreq->lifr_addr);
|
||||
idx = MIN(idx, CONFIG_NETDEV_MAX_IPv6_ADDR - 1);
|
||||
ioctl_set_ipv6addr(dev->d_ipv6[idx].addr, &lreq->lifr_addr);
|
||||
netlink_device_notify_ipaddr(dev, RTM_NEWADDR, AF_INET6,
|
||||
dev->d_ipv6addr, net_ipv6_mask2pref(dev->d_ipv6netmask));
|
||||
dev->d_ipv6[idx].addr, net_ipv6_mask2pref(dev->d_ipv6[idx].mask));
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -928,14 +941,16 @@ static int netdev_ifr_ioctl(FAR struct socket *psock, int cmd,
|
|||
case SIOCGLIFNETMASK: /* Get network mask */
|
||||
{
|
||||
FAR struct lifreq *lreq = (FAR struct lifreq *)req;
|
||||
ioctl_get_ipv6addr(&lreq->lifr_addr, dev->d_ipv6netmask);
|
||||
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;
|
||||
ioctl_set_ipv6addr(dev->d_ipv6netmask, &lreq->lifr_addr);
|
||||
idx = MIN(idx, CONFIG_NETDEV_MAX_IPv6_ADDR - 1);
|
||||
ioctl_set_ipv6addr(dev->d_ipv6[idx].mask, &lreq->lifr_addr);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
@ -1057,16 +1072,53 @@ static int netdev_ifr_ioctl(FAR struct socket *psock, int cmd,
|
|||
break;
|
||||
#endif
|
||||
|
||||
case SIOCSIFADDR: /* Set IP address */
|
||||
#ifdef CONFIG_NET_IPv4
|
||||
if (psock->s_domain != PF_INET6)
|
||||
{
|
||||
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));
|
||||
}
|
||||
#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
|
||||
netlink_device_notify_ipaddr(dev, RTM_DELADDR, AF_INET,
|
||||
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;
|
||||
dev->d_ipaddr = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_NET_IPv6
|
||||
netlink_device_notify_ipaddr(dev, RTM_DELADDR, AF_INET6,
|
||||
dev->d_ipv6addr, net_ipv6_mask2pref(dev->d_ipv6netmask));
|
||||
memset(&dev->d_ipv6addr, 0, sizeof(net_ipv6addr_t));
|
||||
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;
|
||||
|
||||
|
@ -1589,7 +1641,7 @@ static int netdev_ioctl(FAR struct socket *psock, int cmd,
|
|||
* Calculate the ioctl argument buffer length.
|
||||
*
|
||||
* Input Parameters:
|
||||
*
|
||||
* domain The socket domain
|
||||
* cmd The ioctl command
|
||||
*
|
||||
* Returned Value:
|
||||
|
@ -1597,11 +1649,11 @@ static int netdev_ioctl(FAR struct socket *psock, int cmd,
|
|||
*
|
||||
****************************************************************************/
|
||||
|
||||
ssize_t net_ioctl_arglen(int cmd)
|
||||
ssize_t net_ioctl_arglen(uint8_t domain, int cmd)
|
||||
{
|
||||
ssize_t arglen;
|
||||
|
||||
arglen = net_ioctl_ifreq_arglen(cmd);
|
||||
arglen = net_ioctl_ifreq_arglen(domain, cmd);
|
||||
if (arglen > 0)
|
||||
{
|
||||
return arglen;
|
||||
|
|
|
@ -183,7 +183,7 @@ int usrsock_ioctl(FAR struct socket *psock, int cmd, unsigned long arg_)
|
|||
return -ENOTTY;
|
||||
}
|
||||
|
||||
arglen = net_ioctl_arglen(cmd);
|
||||
arglen = net_ioctl_arglen(psock->s_domain, cmd);
|
||||
if (arglen < 0)
|
||||
{
|
||||
return arglen;
|
||||
|
|
Loading…
Reference in a new issue