netlink: add RTM_NEWADDR, RTM_DELADDR and RTM_GETADDR

We have projects that need to sense ip address changes in time,
and set ip address or clear ip address through netlink
so the relevant implementation is added.
The usage and command structure are consistent with linux

Signed-off-by: zhanghongyu <zhanghongyu@xiaomi.com>
This commit is contained in:
zhanghongyu 2023-01-30 16:50:11 +08:00 committed by Xiang Xiao
parent 873abcb9ab
commit fdc6dbf176
8 changed files with 1209 additions and 2 deletions

View File

@ -85,6 +85,8 @@ struct ifaddrmsg
* *
* IFA_FLAGS is a u32 attribute that extends the u8 field ifa_flags. * IFA_FLAGS is a u32 attribute that extends the u8 field ifa_flags.
* If present, the value from struct ifaddrmsg will be ignored. * If present, the value from struct ifaddrmsg will be ignored.
*
* IFA_RT_PRIORITY is a u32 attribute, priority/metric for prefix route
*/ */
enum enum
@ -98,6 +100,7 @@ enum
IFA_CACHEINFO, IFA_CACHEINFO,
IFA_MULTICAST, IFA_MULTICAST,
IFA_FLAGS, IFA_FLAGS,
IFA_RT_PRIORITY,
__IFA_MAX, __IFA_MAX,
}; };

View File

@ -389,6 +389,25 @@
#define RTNLGRP_NSID 28 #define RTNLGRP_NSID 28
#define RTNLGRP_MAX 29 #define RTNLGRP_MAX 29
/**
* nla_type (16 bits)
* +---+---+-------------------------------+
* | N | O | Attribute Type |
* +---+---+-------------------------------+
* N := Carries nested attributes
* O := Payload stored in network byte order
*
* Note: The N and O flag are mutually exclusive.
*/
#define NLA_F_NET_BYTEORDER (1 << 14)
#define NLA_F_NESTED (1 << 15)
#define NLA_TYPE_MASK ~(NLA_F_NESTED | NLA_F_NET_BYTEORDER)
#define NLA_ALIGNTO 4
#define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1))
#define NLA_HDRLEN (NLA_ALIGN(sizeof(struct nlattr)))
/**************************************************************************** /****************************************************************************
* Public Type Definitions * Public Type Definitions
****************************************************************************/ ****************************************************************************/
@ -503,6 +522,41 @@ struct rtmsg
uint32_t rtm_flags; uint32_t rtm_flags;
}; };
/**
* <------- NLA_HDRLEN ------> <-- NLA_ALIGN(payload)-->
* +---------------------+- - -+- - - - - - - - - -+- - -+
* | Header | Pad | Payload | Pad |
* | (struct nlattr) | ing | | ing |
* +---------------------+- - -+- - - - - - - - - -+- - -+
* <-------------- nlattr->nla_len -------------->
*/
struct nlattr
{
uint16_t nla_len;
uint16_t nla_type;
};
/* Generic 32 bitflags attribute content sent to the kernel.
*
* The value is a bitmap that defines the values being set
* The selector is a bitmask that defines which value is legit
*
* Examples:
* value = 0x0, and selector = 0x1
* implies we are selecting bit 1 and we want to set its value to 0.
*
* value = 0x2, and selector = 0x2
* implies we are selecting bit 2 and we want to set its value to 1.
*
*/
struct nla_bitfield32
{
uint32_t value;
uint32_t selector;
};
/**************************************************************************** /****************************************************************************
* Public Function Prototypes * Public Function Prototypes
****************************************************************************/ ****************************************************************************/

View File

@ -839,6 +839,7 @@ static int netdev_ifr_ioctl(FAR struct socket *psock, int cmd,
case SIOCSIFADDR: /* Set IP address */ case SIOCSIFADDR: /* Set IP address */
ioctl_set_ipv4addr(&dev->d_ipaddr, &req->ifr_addr); ioctl_set_ipv4addr(&dev->d_ipaddr, &req->ifr_addr);
netlink_device_notify_ipaddr(dev, RTM_NEWADDR, AF_INET);
break; break;
case SIOCGIFDSTADDR: /* Get P-to-P address */ case SIOCGIFDSTADDR: /* Get P-to-P address */
@ -879,6 +880,7 @@ static int netdev_ifr_ioctl(FAR struct socket *psock, int cmd,
{ {
FAR struct lifreq *lreq = (FAR struct lifreq *)req; FAR struct lifreq *lreq = (FAR struct lifreq *)req;
ioctl_set_ipv6addr(dev->d_ipv6addr, &lreq->lifr_addr); ioctl_set_ipv6addr(dev->d_ipv6addr, &lreq->lifr_addr);
netlink_device_notify_ipaddr(dev, RTM_NEWADDR, AF_INET6);
} }
break; break;
@ -1040,9 +1042,11 @@ static int netdev_ifr_ioctl(FAR struct socket *psock, int cmd,
case SIOCDIFADDR: /* Delete IP address */ case SIOCDIFADDR: /* Delete IP address */
#ifdef CONFIG_NET_IPv4 #ifdef CONFIG_NET_IPv4
dev->d_ipaddr = 0; dev->d_ipaddr = 0;
netlink_device_notify_ipaddr(dev, RTM_DELADDR, AF_INET);
#endif #endif
#ifdef CONFIG_NET_IPv6 #ifdef CONFIG_NET_IPv6
memset(&dev->d_ipv6addr, 0, sizeof(net_ipv6addr_t)); memset(&dev->d_ipv6addr, 0, sizeof(net_ipv6addr_t));
netlink_device_notify_ipaddr(dev, RTM_DELADDR, AF_INET6);
#endif #endif
break; break;

View File

@ -91,6 +91,31 @@ config NETLINK_DISABLE_GETROUTE
---help--- ---help---
RTM_GETROUTE is used to retrieve routing tables. RTM_GETROUTE is used to retrieve routing tables.
config NETLINK_DISABLE_NEWADDR
bool "Disable RTM_NEWADDR support"
default n
---help---
RTM_NEWADDR is used to set netdev address.
config NETLINK_DISABLE_DELADDR
bool "Disable RTM_DELADDR support"
default n
---help---
RTM_DELADDR is used to delete netdev address.
config NETLINK_DISABLE_GETADDR
bool "Disable RTM_GETADDR support"
default n
---help---
RTM_GETADDR is used to get netdev address.
config NETLINK_VALIDATE_POLICY
bool "Enable netlink message policy verification"
default n
---help---
VALIDATE_POLICY is used to make sure the parameters
you pass in are valid.
endif # NETLINK_ROUTE endif # NETLINK_ROUTE
endmenu # Netlink Protocols endmenu # Netlink Protocols
endif # NET_NETLINK endif # NET_NETLINK

View File

@ -26,7 +26,7 @@ SOCK_CSRCS += netlink_sockif.c
NET_CSRCS += netlink_conn.c netlink_notifier.c NET_CSRCS += netlink_conn.c netlink_notifier.c
ifeq ($(CONFIG_NETLINK_ROUTE),y) ifeq ($(CONFIG_NETLINK_ROUTE),y)
NET_CSRCS += netlink_route.c NET_CSRCS += netlink_route.c netlink_attr.c
endif endif
# Include netlink build support # Include netlink build support

View File

@ -45,10 +45,141 @@
#ifndef CONFIG_NETLINK_ROUTE #ifndef CONFIG_NETLINK_ROUTE
# define netlink_device_notify(dev) # define netlink_device_notify(dev)
# define netlink_device_notify_ipaddr(dev, type, domain)
#endif #endif
#ifdef CONFIG_NET_NETLINK #ifdef CONFIG_NET_NETLINK
/**
* nla_for_each_attr - iterate over a stream of attributes
* @pos: loop counter, set to current attribute
* @head: head of attribute stream
* @len: length of attribute stream
* @rem: initialized to len, holds bytes currently remaining in stream
*/
#define nla_for_each_attr(pos, head, len, rem) \
for (pos = head, rem = len; nla_ok(pos, rem); \
pos = nla_next(pos, &(rem)))
/* Always use this macro, this allows later putting the
* message into a separate section or such for things
* like translation or listing all possible messages.
* Currently string formatting is not supported (due
* to the lack of an output buffer.)
*/
#define nl_set_err_msg_attr(extack, attr, msg) \
do \
{ \
static const char __msg[] = (msg); \
FAR struct netlink_ext_ack *__extack = (extack); \
if (__extack) \
{ \
__extack->_msg = __msg; \
__extack->bad_attr = (attr); \
} \
} \
while (0)
/**
* nla_data - head of payload
* @nla: netlink attribute
*/
#define nla_data(nla) ((FAR void *)((FAR char *)(nla) + NLA_HDRLEN))
/**
* nla_len - length of payload
* @nla: netlink attribute
*/
#define nla_len(nla) ((nla)->nla_len - NLA_HDRLEN)
/**
* nla_type - attribute type
* @nla: netlink attribute
*/
#define nla_type(nla) ((nla)->nla_type & NLA_TYPE_MASK)
/**
* nla_ok - check if the netlink attribute fits into the remaining bytes
* @nla: netlink attribute
* @remaining: number of bytes remaining in attribute stream
*/
#define nla_ok(nla, remaining) \
((remaining) >= sizeof(*(nla)) && \
(nla)->nla_len >= sizeof(*(nla)) && \
(nla)->nla_len <= (remaining))
/**
* nlmsg_msg_size - length of netlink message not including padding
* @payload: length of message payload
*/
#define nlmsg_msg_size(payload) (NLMSG_HDRLEN + (payload))
/**
* nlmsg_len - length of message payload
* @nlh: netlink message header
*/
#define nlmsg_len(nlh) ((nlh)->nlmsg_len - NLMSG_HDRLEN)
/**
* nlmsg_attrlen - length of attributes data
* @nlh: netlink message header
* @hdrlen: length of family specific header
*/
#define nlmsg_attrlen(nlh, hdrlen) (nlmsg_len(nlh) - NLMSG_ALIGN(hdrlen))
/**
* nlmsg_data - head of message payload
* @nlh: netlink message header
*/
#define nlmsg_data(nlh) ((FAR void *)((FAR char *)(nlh) + NLMSG_HDRLEN))
/**
* nla_get_in_addr - return payload of IPv4 address attribute
* @nla: IPv4 address netlink attribute
*/
#define nla_get_in_addr(nla) (*(FAR uint32_t *)nla_data(nla))
/**
* nlmsg_attrdata - head of attributes data
* @nlh: netlink message header
* @hdrlen: length of family specific header
*/
#define nlmsg_attrdata(nlh, hdrlen) \
((FAR struct nlattr *)((FAR char *)nlmsg_data(nlh) + NLMSG_ALIGN(hdrlen)))
/**
* nlmsg_parse - parse attributes of a netlink message
* @nlh: netlink message header
* @hdrlen: length of family specific header
* @tb: destination array with maxtype+1 elements
* @maxtype: maximum attribute type to be expected
* @policy: validation policy
* @extack: extended ACK report struct
*
* See nla_parse()
*/
#define nlmsg_parse(nlh, hdrlen, tb, maxtype, policy, extack) \
((nlh)->nlmsg_len < nlmsg_msg_size(hdrlen) ? -EINVAL : \
nla_parse(tb, maxtype, nlmsg_attrdata(nlh, hdrlen), \
nlmsg_attrlen(nlh, hdrlen), policy, extack))
/* this can be increased when necessary - don't expose to userland */
#define NETLINK_MAX_COOKIE_LEN 20
/**************************************************************************** /****************************************************************************
* Public Type Definitions * Public Type Definitions
****************************************************************************/ ****************************************************************************/
@ -79,6 +210,92 @@ struct netlink_conn_s
sq_queue_t resplist; /* Singly linked list of responses */ sq_queue_t resplist; /* Singly linked list of responses */
}; };
/**
* Standard attribute types to specify validation policy
*/
enum
{
NLA_UNSPEC,
NLA_U8,
NLA_U16,
NLA_U32,
NLA_U64,
NLA_STRING,
NLA_FLAG,
NLA_MSECS,
NLA_NESTED,
NLA_NESTED_COMPAT,
NLA_NUL_STRING,
NLA_BINARY,
NLA_S8,
NLA_S16,
NLA_S32,
NLA_S64,
NLA_BITFIELD32,
NLA_TYPE_MAX = NLA_BITFIELD32,
};
/**
* struct netlink_ext_ack - netlink extended ACK report struct
* @_msg: message string to report - don't access directly, use
* %nl_set_err_msg_attr
* @bad_attr: attribute with error
* @cookie: cookie data to return to userspace (for success)
* @cookie_len: actual cookie data length
*/
struct netlink_ext_ack
{
FAR const char *_msg;
FAR const struct nlattr *bad_attr;
uint8_t cookie[NETLINK_MAX_COOKIE_LEN];
uint8_t cookie_len;
};
/**
* struct nla_policy - attribute validation policy
* @type: Type of attribute or NLA_UNSPEC
* @len: Type specific length of payload
*
* Policies are defined as arrays of this struct, the array must be
* accessible by attribute type up to the highest identifier to be expected.
*
* Meaning of `len' field:
* NLA_STRING Maximum length of string
* NLA_NUL_STRING Maximum length of string (excluding NUL)
* NLA_FLAG Unused
* NLA_BINARY Maximum length of attribute payload
* NLA_NESTED Don't use `len' field -- length verification is
* done by checking len of nested header (or empty)
* NLA_NESTED_COMPAT Minimum length of structure payload
* NLA_U8, NLA_U16,
* NLA_U32, NLA_U64,
* NLA_S8, NLA_S16,
* NLA_S32, NLA_S64,
* NLA_MSECS Leaving the length field zero will verify the
* given type fits, using it verifies minimum length
* just like "All other"
* NLA_BITFIELD32 A 32-bit bitmap/bitselector attribute
* All other Minimum length of attribute payload
*
* Example:
* static const struct nla_policy my_policy[ATTR_MAX + 1] = {
* [ATTR_FOO] = { .type = NLA_U16 },
* [ATTR_BAR] = { .type = NLA_STRING, .len = BARSIZ },
* [ATTR_BAZ] = { .len = sizeof(struct mystruct) },
* [ATTR_GOO] = { .type = NLA_BITFIELD32, .validation_data =
* &myvalidflags },
* };
*/
struct nla_policy
{
uint16_t type;
uint16_t len;
FAR void *validation_data;
};
/**************************************************************************** /****************************************************************************
* Public Data * Public Data
****************************************************************************/ ****************************************************************************/
@ -275,6 +492,49 @@ ssize_t netlink_route_sendto(NETLINK_HANDLE handle,
****************************************************************************/ ****************************************************************************/
void netlink_device_notify(FAR struct net_driver_s *dev); void netlink_device_notify(FAR struct net_driver_s *dev);
/****************************************************************************
* Name: netlink_device_notify_ipaddr()
*
* Description:
* Perform the route broadcast for the NETLINK_ROUTE protocol.
*
****************************************************************************/
void netlink_device_notify_ipaddr(FAR struct net_driver_s *dev,
int type, int domain);
/****************************************************************************
* Name: nla_next
*
* Description:
* Next netlink attribute in attribute stream.
*
* Input Parameters:
* nla - netlink attribute.
* remaining - number of bytes remaining in attribute stream.
*
* Returned Value:
* Returns the next netlink attribute in the attribute stream and
* decrements remaining by the size of the current attribute.
*
****************************************************************************/
FAR struct nlattr *nla_next(FAR const struct nlattr *nla,
FAR int *remaining);
/****************************************************************************
* Name: nla_parse
*
* Description:
* Parse the nested netlink attribute.
*
****************************************************************************/
int nla_parse(FAR struct nlattr **tb, int maxtype,
FAR const struct nlattr *head,
int len, FAR const struct nla_policy *policy,
FAR struct netlink_ext_ack *extack);
#endif #endif
#undef EXTERN #undef EXTERN

332
net/netlink/netlink_attr.c Normal file
View File

@ -0,0 +1,332 @@
/****************************************************************************
* net/netlink/netlink_attr.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <sys/types.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <unistd.h>
#include <nuttx/net/netlink.h>
#include "netlink.h"
/****************************************************************************
* Private Data
****************************************************************************/
#ifdef CONFIG_NETLINK_VALIDATE_POLICY
static const uint8_t g_nla_attr_len[NLA_TYPE_MAX + 1] =
{
0,
sizeof(uint8_t),
sizeof(uint16_t),
sizeof(uint32_t),
sizeof(uint64_t),
0, 0, 0, 0, 0, 0, 0,
sizeof(int8_t),
sizeof(int16_t),
sizeof(int32_t),
sizeof(int64_t),
};
static const uint8_t g_nla_attr_minlen[NLA_TYPE_MAX + 1] =
{
0,
sizeof(uint8_t),
sizeof(uint16_t),
sizeof(uint32_t),
sizeof(uint64_t),
0, 0,
sizeof(uint64_t),
NLA_HDRLEN,
0, 0, 0,
sizeof(int8_t),
sizeof(int16_t),
sizeof(int32_t),
sizeof(int64_t),
};
/****************************************************************************
* Private Functions
****************************************************************************/
static int validate_nla_bitfield32(FAR const struct nlattr *nla,
FAR uint32_t *valid_flags_mask)
{
FAR const struct nla_bitfield32 *bf = nla_data(nla);
if (!valid_flags_mask)
{
return -EINVAL;
}
/* disallow invalid bit selector */
if (bf->selector & ~*valid_flags_mask)
{
return -EINVAL;
}
/* disallow invalid bit values */
if (bf->value & ~*valid_flags_mask)
{
return -EINVAL;
}
/* disallow valid bit values that are not selected */
if (bf->value & ~bf->selector)
{
return -EINVAL;
}
return 0;
}
static int validate_nla(FAR const struct nlattr *nla, int maxtype,
FAR const struct nla_policy *policy)
{
FAR const struct nla_policy *pt;
int minlen = 0;
int attrlen = nla_len(nla);
int type = nla_type(nla);
if (type <= 0 || type > maxtype)
{
return -EINVAL;
}
pt = &policy[type];
DEBUGASSERT(pt->type <= NLA_TYPE_MAX);
if (g_nla_attr_len[pt->type] && attrlen != g_nla_attr_len[pt->type])
{
nwarn("netlink: '%d': attribute type %d has an invalid length.\n",
getpid(), type);
return -EINVAL;
}
switch (pt->type)
{
case NLA_FLAG:
if (attrlen > 0)
{
return -ERANGE;
}
break;
case NLA_BITFIELD32:
if (attrlen != sizeof(struct nla_bitfield32))
{
return -ERANGE;
}
if (validate_nla_bitfield32(nla, pt->validation_data) != 0)
{
return -EINVAL;
}
break;
case NLA_NUL_STRING:
if (pt->len)
{
minlen = MIN(attrlen, pt->len + 1);
}
else
{
minlen = attrlen;
}
if (!minlen || memchr(nla_data(nla), '\0', minlen) == NULL)
{
return -EINVAL;
}
/* fall through */
case NLA_STRING:
if (attrlen < 1)
{
return -ERANGE;
}
if (pt->len)
{
FAR char *buf = nla_data(nla);
if (buf[attrlen - 1] == '\0')
{
attrlen--;
}
if (attrlen > pt->len)
{
return -ERANGE;
}
}
break;
case NLA_BINARY:
if (pt->len && attrlen > pt->len)
{
return -ERANGE;
}
break;
case NLA_NESTED_COMPAT:
if (attrlen < pt->len)
{
return -ERANGE;
}
if (attrlen < NLA_ALIGN(pt->len))
{
break;
}
if (attrlen < NLA_ALIGN(pt->len) + NLA_HDRLEN)
{
return -ERANGE;
}
nla = nla_data(nla) + NLA_ALIGN(pt->len);
if (attrlen < NLA_ALIGN(pt->len) + NLA_HDRLEN + nla_len(nla))
{
return -ERANGE;
}
break;
case NLA_NESTED:
/* a nested attributes is allowed to be empty; if its not,
* it must have a size of at least NLA_HDRLEN.
*/
if (attrlen == 0)
{
break;
}
default:
if (pt->len)
{
minlen = pt->len;
}
else if (pt->type != NLA_UNSPEC)
{
minlen = g_nla_attr_minlen[pt->type];
}
if (attrlen < minlen)
{
return -ERANGE;
}
}
return 0;
}
#else
# define validate_nla(nla, maxtype, policy) 0
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: nla_next
*
* Description:
* Next netlink attribute in attribute stream.
*
* Input Parameters:
* nla - netlink attribute.
* remaining - number of bytes remaining in attribute stream.
*
* Returned Value:
* Returns the next netlink attribute in the attribute stream and
* decrements remaining by the size of the current attribute.
*
****************************************************************************/
FAR struct nlattr *nla_next(FAR const struct nlattr *nla,
FAR int *remaining)
{
unsigned int totlen = NLA_ALIGN(nla->nla_len);
*remaining -= totlen;
return (FAR struct nlattr *)((FAR char *)nla + totlen);
}
/****************************************************************************
* Name: nla_parse
*
* Description:
* Parse the nested netlink attribute.
*
****************************************************************************/
int nla_parse(FAR struct nlattr **tb, int maxtype,
FAR const struct nlattr *head,
int len, FAR const struct nla_policy *policy,
FAR struct netlink_ext_ack *extack)
{
FAR const struct nlattr *nla;
int rem;
int err = 0;
memset(tb, 0, sizeof(FAR struct nlattr *) * (maxtype + 1));
nla_for_each_attr(nla, head, len, rem)
{
uint16_t type = nla_type(nla);
if (type > 0 && type <= maxtype)
{
err = validate_nla(nla, maxtype, policy);
if (err < 0)
{
nl_set_err_msg_attr(extack, nla,
"Attribute failed policy validation");
goto errout;
}
tb[type] = (FAR struct nlattr *)nla;
}
}
if (rem > 0)
{
nwarn("netlink: %d bytes leftover after parsing attributes in "
"pid `%d'.\n", rem, getpid());
}
errout:
return err;
}

View File

@ -44,6 +44,7 @@
#include "neighbor/neighbor.h" #include "neighbor/neighbor.h"
#include "route/route.h" #include "route/route.h"
#include "netlink/netlink.h" #include "netlink/netlink.h"
#include "utils/utils.h"
#ifdef CONFIG_NETLINK_ROUTE #ifdef CONFIG_NETLINK_ROUTE
@ -150,6 +151,26 @@ struct getroute_recvfrom_ipv6resplist_s
struct getroute_recvfrom_ipv6response_s payload; struct getroute_recvfrom_ipv6response_s payload;
}; };
/* RTM_GETADDR: Get the specified network device address info */
struct getaddr_recvfrom_response_s
{
struct nlmsghdr hdr;
struct ifaddrmsg ifaddr;
struct rtattr attr;
#ifndef CONFIG_NET_IPv6
struct in_addr local_addr; /* IFA_LOCAL is the only attribute supported */
#else
struct in6_addr local_addr; /* IFA_LOCAL is the only attribute supported */
#endif
};
struct getaddr_recvfrom_rsplist_s
{
sq_entry_t flink;
struct getaddr_recvfrom_response_s payload;
};
/* netdev_foreach() callback */ /* netdev_foreach() callback */
struct nlroute_sendto_request_s struct nlroute_sendto_request_s
@ -164,6 +185,54 @@ struct nlroute_info_s
FAR const struct nlroute_sendto_request_s *req; FAR const struct nlroute_sendto_request_s *req;
}; };
/****************************************************************************
* Private Data
****************************************************************************/
#ifdef CONFIG_NETLINK_VALIDATE_POLICY
# ifdef CONFIG_NET_IPv4
static const struct nla_policy g_ifa_ipv4_policy[] =
{
{0}, /* IFA_UNSPEC */
{NLA_U32, 0, NULL}, /* IFA_ADDRESS */
{NLA_U32, 0, NULL}, /* IFA_LOCAL */
{NLA_STRING, IFNAMSIZ - 1, NULL}, /* IFA_LABEL */
{NLA_U32, 0, NULL}, /* IFA_BROADCAST */
{0}, /* IFA_ANYCAST */
{NLA_UNSPEC, sizeof(struct ifa_cacheinfo), NULL}, /* IFA_CACHEINFO */
{0}, /* IFA_MULTICAST */
{NLA_U32, 0, NULL}, /* IFA_FLAGS */
{NLA_U32, 0, NULL}, /* IFA_RT_PRIORITY */
};
static_assert(sizeof(g_ifa_ipv4_policy) / sizeof(g_ifa_ipv4_policy[0]) ==
IFA_MAX + 1, "The policy definition has changed,"
" please check it");
# endif
# ifdef CONFIG_NET_IPv6
static const struct nla_policy g_ifa_ipv6_policy[] =
{
{0}, /* IFA_UNSPEC */
{0, sizeof(struct in6_addr), NULL}, /* IFA_ADDRESS */
{0, sizeof(struct in6_addr), NULL}, /* IFA_LOCAL */
{0}, /* IFA_LABEL */
{0}, /* IFA_BROADCAST */
{0}, /* IFA_ANYCAST */
{NLA_UNSPEC, sizeof(struct ifa_cacheinfo), NULL}, /* IFA_CACHEINFO */
{0}, /* IFA_MULTICAST */
{0, sizeof(uint32_t), NULL}, /* IFA_FLAGS */
{0, sizeof(uint32_t), NULL}, /* IFA_RT_PRIORITY */
};
static_assert(sizeof(g_ifa_ipv6_policy) / sizeof(g_ifa_ipv6_policy[0]) ==
IFA_MAX + 1, "The policy definition has changed,"
" please check it");
# endif
#else
# define g_ifa_ipv4_policy NULL
# define g_ifa_ipv6_policy NULL
#endif
/**************************************************************************** /****************************************************************************
* Private Functions * Private Functions
****************************************************************************/ ****************************************************************************/
@ -224,6 +293,96 @@ netlink_get_device(FAR struct net_driver_s *dev,
} }
#endif #endif
#ifdef CONFIG_NET_IPv4
static uint32_t make_mask(int prefixlen)
{
if (prefixlen)
{
return HTONL(UINT32_MAX << (32 - prefixlen));
}
return 0;
}
static uint8_t mask_len(uint32_t nmask)
{
uint8_t prefixlen = 0;
uint32_t hmask = NTOHL(nmask);
for (; hmask; ++prefixlen, hmask <<= 1);
return prefixlen;
}
#endif
/****************************************************************************
* Name: netlink_get_ifaddr
*
* Description:
* Generate one interface address response.
*
****************************************************************************/
#ifndef CONFIG_NETLINK_DISABLE_GETLINK
static FAR struct netlink_response_s *
netlink_get_ifaddr(FAR struct net_driver_s *dev, int domain, int type,
FAR const struct nlroute_sendto_request_s *req)
{
FAR struct getaddr_recvfrom_rsplist_s *alloc;
FAR struct getaddr_recvfrom_response_s *resp;
/* Allocate the response buffer */
alloc = (FAR struct getaddr_recvfrom_rsplist_s *)
kmm_zalloc(sizeof(struct getaddr_recvfrom_rsplist_s));
if (alloc == NULL)
{
nerr("ERROR: Failed to allocate response buffer.\n");
return NULL;
}
/* Initialize the response buffer */
resp = &alloc->payload;
resp->hdr.nlmsg_len = sizeof(struct getaddr_recvfrom_response_s);
resp->hdr.nlmsg_type = type;
resp->hdr.nlmsg_flags = req ? req->hdr.nlmsg_flags : 0;
resp->hdr.nlmsg_seq = req ? req->hdr.nlmsg_seq : 0;
resp->hdr.nlmsg_pid = req ? req->hdr.nlmsg_pid : 0;
resp->ifaddr.ifa_family = domain;
#ifdef CONFIG_NETDEV_IFINDEX
resp->ifaddr.ifa_index = dev->d_ifindex;
#endif
resp->ifaddr.ifa_flags = IFA_F_PERMANENT;
resp->ifaddr.ifa_scope = RT_SCOPE_UNIVERSE;
resp->attr.rta_type = IFA_LOCAL;
#ifdef CONFIG_NET_IPv4
if (domain == AF_INET)
{
resp->attr.rta_len = RTA_LENGTH(sizeof(struct in_addr));
memcpy(&resp->local_addr, &dev->d_ipaddr, sizeof(struct in_addr));
resp->ifaddr.ifa_prefixlen = mask_len(dev->d_netmask);
}
#endif
#ifdef CONFIG_NET_IPv6
if (domain == AF_INET6)
{
resp->attr.rta_len = RTA_LENGTH(sizeof(struct in6_addr));
memcpy(&resp->local_addr, dev->d_ipv6addr, sizeof(struct in6_addr));
resp->ifaddr.ifa_prefixlen = net_ipv6_mask2pref(dev->d_ipv6netmask);
}
#endif
/* Finally, return the response */
return (FAR struct netlink_response_s *)alloc;
}
#endif
/**************************************************************************** /****************************************************************************
* Name: netlink_get_terminator * Name: netlink_get_terminator
* *
@ -684,6 +843,251 @@ static int netlink_get_ip6vroute(NETLINK_HANDLE handle,
} }
#endif #endif
/****************************************************************************
* Name: netlink_new_ipv4addr
*
* Description:
* Set the ipv4 address.
*
****************************************************************************/
#if defined(CONFIG_NET_IPv4) && !defined(CONFIG_NETLINK_DISABLE_NEWADDR)
static int netlink_new_ipv4addr(NETLINK_HANDLE handle,
FAR const struct nlmsghdr *nlmsg)
{
FAR struct net_driver_s *dev;
FAR struct ifaddrmsg *ifm = NLMSG_DATA(nlmsg);
FAR struct nlattr *tb[IFA_MAX + 1];
struct netlink_ext_ack extack;
int ret;
ret = nlmsg_parse(nlmsg, sizeof(*ifm), tb, IFA_MAX, g_ifa_ipv4_policy,
&extack);
if (ret < 0)
{
return ret;
}
if (ifm->ifa_prefixlen > 32 || !tb[IFA_LOCAL])
{
return -EINVAL;
}
net_lock();
dev = netdev_findbyindex(ifm->ifa_index);
if (dev == NULL)
{
net_unlock();
return -ENODEV;
}
dev->d_ipaddr = nla_get_in_addr(tb[IFA_LOCAL]);
dev->d_netmask = make_mask(ifm->ifa_prefixlen);
netlink_device_notify_ipaddr(dev, RTM_NEWADDR, AF_INET);
net_unlock();
return OK;
}
#endif
/****************************************************************************
* Name: netlink_new_ipv6addr
*
* Description:
* Set the ipv6 address.
*
****************************************************************************/
#if defined(CONFIG_NET_IPv6) && !defined(CONFIG_NETLINK_DISABLE_NEWADDR)
static int netlink_new_ipv6addr(NETLINK_HANDLE handle,
FAR const struct nlmsghdr *nlmsg)
{
FAR struct net_driver_s *dev;
FAR struct ifaddrmsg *ifm = NLMSG_DATA(nlmsg);
FAR struct nlattr *tb[IFA_MAX + 1];
struct netlink_ext_ack extack;
int ret;
ret = nlmsg_parse(nlmsg, sizeof(*ifm), tb, IFA_MAX, g_ifa_ipv6_policy,
&extack);
if (ret < 0)
{
return ret;
}
if (ifm->ifa_prefixlen > 128 || !tb[IFA_LOCAL])
{
return -EINVAL;
}
net_lock();
dev = netdev_findbyindex(ifm->ifa_index);
if (dev == NULL)
{
net_unlock();
return -ENODEV;
}
memcpy(dev->d_ipv6addr, nla_data(tb[IFA_LOCAL]), 16);
net_ipv6_pref2mask(ifm->ifa_prefixlen, dev->d_ipv6netmask);
netlink_device_notify_ipaddr(dev, RTM_NEWADDR, AF_INET6);
net_unlock();
return OK;
}
#endif
/****************************************************************************
* Name: netlink_del_ipv4addr
*
* Description:
* Clear the ipv4 address.
*
****************************************************************************/
#if defined(CONFIG_NET_IPv4) && !defined(CONFIG_NETLINK_DISABLE_DELADDR)
static int netlink_del_ipv4addr(NETLINK_HANDLE handle,
FAR const struct nlmsghdr *nlmsg)
{
FAR struct net_driver_s *dev;
FAR struct ifaddrmsg *ifm = NLMSG_DATA(nlmsg);
FAR struct nlattr *tb[IFA_MAX + 1];
struct netlink_ext_ack extack;
int ret;
ret = nlmsg_parse(nlmsg, sizeof(*ifm), tb, IFA_MAX, g_ifa_ipv4_policy,
&extack);
if (ret < 0)
{
return ret;
}
net_lock();
dev = netdev_findbyindex(ifm->ifa_index);
if (dev == NULL)
{
net_unlock();
return -ENODEV;
}
if (tb[IFA_LOCAL] && dev->d_ipaddr != nla_get_in_addr(tb[IFA_LOCAL]))
{
net_unlock();
return -EADDRNOTAVAIL;
}
dev->d_ipaddr = 0;
netlink_device_notify_ipaddr(dev, RTM_DELADDR, AF_INET);
net_unlock();
return OK;
}
#endif
/****************************************************************************
* Name: netlink_del_ipv6addr
*
* Description:
* Clear the ipv6 address.
*
****************************************************************************/
#if defined(CONFIG_NET_IPv6) && !defined(CONFIG_NETLINK_DISABLE_DELADDR)
static int netlink_del_ipv6addr(NETLINK_HANDLE handle,
FAR const struct nlmsghdr *nlmsg)
{
FAR struct net_driver_s *dev;
FAR struct ifaddrmsg *ifm = NLMSG_DATA(nlmsg);
FAR struct nlattr *tb[IFA_MAX + 1];
struct netlink_ext_ack extack;
int ret;
ret = nlmsg_parse(nlmsg, sizeof(*ifm), tb, IFA_MAX, g_ifa_ipv6_policy,
&extack);
if (ret < 0)
{
return ret;
}
net_lock();
dev = netdev_findbyindex(ifm->ifa_index);
if (dev == NULL)
{
net_unlock();
return -ENODEV;
}
if (tb[IFA_LOCAL] && dev->d_ipaddr != nla_get_in_addr(tb[IFA_LOCAL]))
{
net_unlock();
return -EADDRNOTAVAIL;
}
memset(&dev->d_ipv6addr, 0, sizeof(net_ipv6addr_t));
netlink_device_notify_ipaddr(dev, RTM_DELADDR, AF_INET6);
net_unlock();
return OK;
}
#endif
/****************************************************************************
* Name: netlink_get_addr
*
* Description:
* Clear the ipv4/ipv6 address.
*
****************************************************************************/
#ifndef CONFIG_NETLINK_DISABLE_GETADDR
static int netlink_addr_callback(FAR struct net_driver_s *dev,
FAR void *arg)
{
FAR struct nlroute_info_s *info = arg;
FAR struct netlink_response_s *resp;
resp = netlink_get_ifaddr(dev, info->req->gen.rtgen_family, RTM_NEWADDR,
info->req);
if (resp == NULL)
{
return -ENOMEM;
}
netlink_add_response(info->handle, resp);
return OK;
}
static int netlink_get_addr(NETLINK_HANDLE handle,
FAR const struct nlroute_sendto_request_s *req)
{
struct nlroute_info_s info;
int ret;
/* Visit each device */
info.handle = handle;
info.req = req;
net_lock();
ret = netdev_foreach(netlink_addr_callback, &info);
net_unlock();
if (ret < 0)
{
return ret;
}
return netlink_add_terminator(handle, req);
}
#endif
/**************************************************************************** /****************************************************************************
* Public Functions * Public Functions
****************************************************************************/ ****************************************************************************/
@ -780,6 +1184,78 @@ ssize_t netlink_route_sendto(NETLINK_HANDLE handle,
break; break;
#endif #endif
#ifndef CONFIG_NETLINK_DISABLE_NEWADDR
/* Set the IPv4 or IPv6 address */
case RTM_NEWADDR:
#ifdef CONFIG_NET_IPv4
if (req->gen.rtgen_family == AF_INET)
{
ret = netlink_new_ipv4addr(handle, nlmsg);
}
else
#endif
#ifdef CONFIG_NET_IPv6
if (req->gen.rtgen_family == AF_INET6)
{
ret = netlink_new_ipv6addr(handle, nlmsg);
}
else
#endif
{
ret = -EAFNOSUPPORT;
}
break;
#endif
#ifndef CONFIG_NETLINK_DISABLE_DELADDR
/* Clear the IPv4 or IPv6 address */
case RTM_DELADDR:
#ifdef CONFIG_NET_IPv4
if (req->gen.rtgen_family == AF_INET)
{
ret = netlink_del_ipv4addr(handle, nlmsg);
}
else
#endif
#ifdef CONFIG_NET_IPv6
if (req->gen.rtgen_family == AF_INET6)
{
ret = netlink_del_ipv6addr(handle, nlmsg);
}
else
#endif
{
ret = -EAFNOSUPPORT;
}
break;
#endif
#ifndef CONFIG_NETLINK_DISABLE_GETADDR
/* Get the IPv4 or IPv6 address */
case RTM_GETADDR:
#ifdef CONFIG_NET_IPv4
if (req->gen.rtgen_family == AF_INET)
{
ret = netlink_get_addr(handle, req);
}
else
#endif
#ifdef CONFIG_NET_IPv6
if (req->gen.rtgen_family == AF_INET6)
{
ret = netlink_get_addr(handle, req);
}
else
#endif
{
ret = -EAFNOSUPPORT;
}
break;
#endif
default: default:
ret = -ENOSYS; ret = -ENOSYS;
break; break;
@ -824,4 +1300,57 @@ void netlink_device_notify(FAR struct net_driver_s *dev)
} }
#endif #endif
/****************************************************************************
* Name: netlink_device_notify_ipaddr()
*
* Description:
* Perform the route broadcast for the NETLINK_ROUTE protocol.
*
****************************************************************************/
#if !defined(CONFIG_NETLINK_DISABLE_NEWADDR) || \
!defined(CONFIG_NETLINK_DISABLE_DELADDR) || \
!defined(CONFIG_NETLINK_DISABLE_GETADDR)
void netlink_device_notify_ipaddr(FAR struct net_driver_s *dev,
int type, int domain)
{
FAR struct netlink_response_s *resp;
int group;
DEBUGASSERT(dev != NULL);
resp = netlink_get_ifaddr(dev, domain, type, NULL);
if (resp != NULL)
{
#ifdef CONFIG_NET_IPv4
if (domain == AF_INET)
{
group = RTNLGRP_IPV4_IFADDR;
}
else
#endif
#ifdef CONFIG_NET_IPv6
if (domain == AF_INET6)
{
group = RTNLGRP_IPV6_IFADDR;
}
else
#endif
{
nwarn("netlink_device_notify_ipaddr unknown type %d domain %d\n",
type, domain);
return;
}
netlink_add_broadcast(group, resp);
resp = netlink_get_terminator(NULL);
if (resp != NULL)
{
netlink_add_broadcast(group, resp);
}
}
}
#endif
#endif /* CONFIG_NETLINK_ROUTE */ #endif /* CONFIG_NETLINK_ROUTE */