715 lines
19 KiB
C
715 lines
19 KiB
C
/* wrnad_neigh.c - Wind River network acceleration daemon (NAD) */
|
|
|
|
/*
|
|
* Copyright (c) 2010 Wind River Systems, Inc.
|
|
*
|
|
* The right to copy, distribute or otherwise make use of this software
|
|
* may be licensed only pursuant to the terms of an applicable Wind River
|
|
* license agreement.
|
|
*/
|
|
|
|
/* includes */
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
#include <syslog.h>
|
|
#include <string.h>
|
|
|
|
#include "wrnad.h"
|
|
|
|
#ifndef IPWRNAD
|
|
|
|
#include <unistd.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/ip6.h>
|
|
#include <netinet/icmp6.h>
|
|
|
|
#include <linux/netlink.h>
|
|
#include <linux/rtnetlink.h>
|
|
|
|
#endif
|
|
|
|
/* defines */
|
|
|
|
/* typedefs */
|
|
|
|
/* imports */
|
|
|
|
/* globals */
|
|
|
|
/* locals */
|
|
|
|
#ifdef WRNAD_IDENTIFY_ROUTERS
|
|
|
|
/******************************************************************************
|
|
*
|
|
* wrnad_neighbor_is_router - check if the neigbor is a router
|
|
*
|
|
* RETURNS: 1 if the neighbor is a router, 0 if not, but -1 if the address
|
|
* family is neither AF_INET nor AF_INET6.
|
|
*
|
|
* ERRNO: N/A
|
|
*/
|
|
|
|
static int
|
|
wrnad_neighbor_is_router(Wrnad_proxy_interface *proxy,
|
|
int family, uint32_t *addr)
|
|
{
|
|
unsigned int ix;
|
|
if (family == AF_INET)
|
|
{
|
|
ip4_item_t *ip4 = hash_lookup_ip4addr(&proxy->v4_routers, addr, &ix);
|
|
return ip4 != NULL;
|
|
}
|
|
if (family == AF_INET6)
|
|
{
|
|
ip6_item_t *ip6 = hash_lookup_ip6addr(&proxy->v6_routers, addr, NULL);
|
|
return ip6 != NULL;
|
|
}
|
|
return -1;
|
|
}
|
|
#endif /* WRNAD_IDENTIFY_ROUTERS */
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* wrnad_rtnl_neigh_statestr - find rtnetlink neighbour state in string format
|
|
*
|
|
* This routine returns the rtnetlink neighbour state as a string
|
|
*
|
|
* RETURNS: string representation of rtnetlink neighbour state.
|
|
*
|
|
* ERRNO: N/A
|
|
*/
|
|
|
|
static const char *
|
|
wrnad_rtnl_neigh_statestr(int state)
|
|
{
|
|
static char statestr[32];
|
|
|
|
switch (state)
|
|
{
|
|
case NUD_INCOMPLETE:
|
|
return "INCOMPLETE";
|
|
case NUD_REACHABLE:
|
|
return "REACHABLE";
|
|
case NUD_STALE:
|
|
return "STALE";
|
|
case NUD_DELAY:
|
|
return "DELAY";
|
|
case NUD_PROBE:
|
|
return "PROBE";
|
|
case NUD_FAILED:
|
|
return "FAILED";
|
|
case NUD_NOARP:
|
|
return "NOARP";
|
|
case NUD_PERMANENT:
|
|
return "PERMANENT";
|
|
default:
|
|
sprintf(statestr, "%d", state);
|
|
return statestr;
|
|
}
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* wrnad_rtnl_neigh_flagsstr - find rtnetlink neigh flags in string format
|
|
*
|
|
* This routine returns the rtnetlink neigh flags as a string
|
|
*
|
|
* RETURNS: string representation of rtnetlink neigh flags.
|
|
*
|
|
* ERRNO: N/A
|
|
*/
|
|
|
|
static const char *
|
|
wrnad_rtnl_neigh_flagsstr(int flags)
|
|
{
|
|
static char flagsstr[256];
|
|
int i;
|
|
|
|
flagsstr[0] = '<';
|
|
i = 1;
|
|
if (flags == 0)
|
|
i += sprintf(&flagsstr[i], "%s", "0,");
|
|
#if 0
|
|
if (flags & NTF_USE)
|
|
i += sprintf(&flagsstr[i], "%s", "NTF,");
|
|
#endif
|
|
if (flags & NTF_PROXY)
|
|
i += sprintf(&flagsstr[i], "%s", "PROXY,");
|
|
if (flags & NTF_ROUTER)
|
|
i += sprintf(&flagsstr[i], "%s", "ROUTER,");
|
|
|
|
flagsstr[i-1] = '>'; /* remove last comma */
|
|
return flagsstr;
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* wrnad_rtnl_neigh_typestr - find rtnetlink neighbour type in string format
|
|
*
|
|
* This routine returns the rtnetlink neighbour type as a string
|
|
*
|
|
* RETURNS: string representation of rtnetlink neighbour type.
|
|
*
|
|
* ERRNO: N/A
|
|
*/
|
|
|
|
static const char *
|
|
wrnad_rtnl_neigh_typestr(int type)
|
|
{
|
|
static char typestr[32];
|
|
|
|
switch (type)
|
|
{
|
|
case RTN_UNSPEC:
|
|
return "RTN_UNSPEC";
|
|
case RTN_UNICAST:
|
|
return "RTN_UNICAST";
|
|
case RTN_LOCAL:
|
|
return "RTN_LOCAL";
|
|
case RTN_BROADCAST:
|
|
return "RTN_BROADCAST";
|
|
case RTN_ANYCAST:
|
|
return "RTN_ANYCAST";
|
|
case RTN_MULTICAST:
|
|
return "RTN_MULTICAST";
|
|
case RTN_BLACKHOLE:
|
|
return "RTN_BLACKHOLE";
|
|
case RTN_UNREACHABLE:
|
|
return "RTN_UNREACHABLE";
|
|
case RTN_PROHIBIT:
|
|
return "RTN_PROHIBIT";
|
|
case RTN_THROW:
|
|
return "RTN_THROW";
|
|
case RTN_NAT:
|
|
return "RTN_NAT";
|
|
case RTN_XRESOLVE:
|
|
return "RTN_XRESOLVE";
|
|
default:
|
|
sprintf(typestr, "%d", type);
|
|
return typestr;
|
|
}
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* wrnad_rtnl_neigh_attrstr - find rtnetlink neighbour attribute in string format
|
|
*
|
|
* This routine returns the rtnetlink neighbour attribute as a string
|
|
*
|
|
* RETURNS: string representation of rtnetlink neighbour attribute.
|
|
*
|
|
* ERRNO: N/A
|
|
*/
|
|
|
|
static const char *
|
|
wrnad_rtnl_neigh_attrstr(int attr)
|
|
{
|
|
static char attrstr[32];
|
|
|
|
switch (attr)
|
|
{
|
|
case NDA_UNSPEC:
|
|
return "NDA_UNSPEC";
|
|
case NDA_DST:
|
|
return "NDA_DST";
|
|
case NDA_LLADDR:
|
|
return "NDA_LLADDR";
|
|
case NDA_CACHEINFO:
|
|
return "NDA_CACHEINFO";
|
|
#ifdef NDA_PROBES
|
|
case NDA_PROBES:
|
|
return "NDA_PROBES";
|
|
#endif
|
|
default:
|
|
sprintf(attrstr, "%d", attr);
|
|
return attrstr;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* wrnad_rtnl_neigh_scan - scan rtnetlink socket for neighbour messages
|
|
*
|
|
* This routine scans neighbour messages received on a rtnetlink socket
|
|
*
|
|
* RETURNS: 0 if exited normally, else -1.
|
|
*
|
|
* ERRNO: N/A
|
|
*/
|
|
static int
|
|
wrnad_rtnl_neigh_scan(struct ndmsg *nd, int len, uint32_t **ppipaddr)
|
|
{
|
|
struct rtattr *rta;
|
|
char buf[64];
|
|
|
|
rta = (struct rtattr *)((char *)nd + NLMSG_ALIGN(sizeof(struct ndmsg)));
|
|
while (RTA_OK(rta, len))
|
|
{
|
|
wrnad_log(LOG_DEBUG, "Rtnetlink NEIGH rtattr: rta_len=%d, rta_type=%s.",
|
|
rta->rta_len, wrnad_rtnl_neigh_attrstr(rta->rta_type));
|
|
|
|
if (rta->rta_type == NDA_DST)
|
|
{
|
|
*ppipaddr = RTA_DATA(rta);
|
|
if (inet_ntop(nd->ndm_family, RTA_DATA(rta), buf, sizeof(buf)) != NULL)
|
|
{
|
|
wrnad_log(LOG_DEBUG, "Rtnetlink NEIGH attribute NDA_DST: %s", buf);
|
|
}
|
|
else
|
|
{
|
|
wrnad_log(LOG_WARNING, "Rtnetlink NEIGH attribute NDA_DST: address conversion failed!");
|
|
}
|
|
}
|
|
else if (rta->rta_type == NDA_LLADDR)
|
|
{
|
|
wrnad_rtnl_eth_ntop(RTA_DATA(rta), buf);
|
|
wrnad_log(LOG_DEBUG, "Rtnetlink NEIGH attribute NDA_LLADDR: %s", buf);
|
|
}
|
|
|
|
rta = RTA_NEXT(rta, len);
|
|
}
|
|
|
|
if (len)
|
|
{
|
|
wrnad_log(LOG_WARNING, "Rtnetlink NEIGH attribute malformed! len=%d rta_len=%d", len, rta->rta_len);
|
|
return -1;
|
|
}
|
|
|
|
wrnad_log(LOG_DEBUG, "Rtnetlink NEIGH message scanned ok.");
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* wrnad_rtnl_neigh_handler - rtnetlink handler for neighbour messages
|
|
*
|
|
* This routine handles rtnetlink neighbour messages.
|
|
*
|
|
* RETURNS: -1 = failed
|
|
* 0 = skip
|
|
* 1 = action
|
|
*
|
|
* ERRNO: N/A
|
|
*/
|
|
|
|
int
|
|
wrnad_rtnl_neigh_handler(struct nlmsghdr *nlh)
|
|
{
|
|
struct ndmsg *nd;
|
|
int len, ret;
|
|
uint32_t *pipaddr = NULL;
|
|
Wrnad_proxy_interface *proxy;
|
|
|
|
nd = NLMSG_DATA(nlh);
|
|
len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(struct ndmsg));
|
|
if (len < 0)
|
|
{
|
|
wrnad_log(LOG_WARNING, "Rtnetlink message type %s has wrong nlmsg length %d!",
|
|
wrnad_rtnl_typestr(nlh->nlmsg_type), nlh->nlmsg_len);
|
|
return -1;
|
|
}
|
|
|
|
wrnad_log(LOG_DEBUG, "Rtnetlink NEIGH operation header: family=%s, ifindex=%d, state=%s, flags=%s, type=%s.",
|
|
wrnad_rtnl_familystr(nd->ndm_family),
|
|
nd->ndm_ifindex,
|
|
wrnad_rtnl_neigh_statestr(nd->ndm_state),
|
|
wrnad_rtnl_neigh_flagsstr(nd->ndm_flags),
|
|
wrnad_rtnl_neigh_typestr(nd->ndm_type));
|
|
|
|
ret = wrnad_rtnl_neigh_scan(nd, len, &pipaddr);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
proxy = wrnad_rtnl_check_proxy_interface(nd->ndm_ifindex);
|
|
if (proxy == NULL)
|
|
{
|
|
wrnad_log(LOG_DEBUG, "Rtnetlink message type %s skipped on interface %d",
|
|
wrnad_rtnl_typestr(nlh->nlmsg_type), nd->ndm_ifindex);
|
|
return 0;
|
|
}
|
|
if (pipaddr == NULL)
|
|
{
|
|
wrnad_log(LOG_WARNING, "No NDA_DST address in %s message on interface %d\n",
|
|
wrnad_rtnl_typestr(nlh->nlmsg_type), nd->ndm_ifindex);
|
|
return 0;
|
|
}
|
|
|
|
/* Accept message */
|
|
wrnad_log(LOG_INFO, "Rtnetlink message type %s accepted on interface %s",
|
|
wrnad_rtnl_typestr(nlh->nlmsg_type), proxy->ifname);
|
|
|
|
/* IPNET has no FAILED state so change the request into a DELNEIGH */
|
|
if (nd->ndm_state == NUD_FAILED)
|
|
nlh->nlmsg_type = RTM_DELNEIGH;
|
|
|
|
#ifdef WRNAD_IDENTIFY_ROUTERS
|
|
if ((nd->ndm_flags & NTF_ROUTER) ||
|
|
wrnad_neighbor_is_router(proxy, nd->ndm_family, pipaddr))
|
|
{
|
|
wrnad_log(LOG_INFO, "Marking as a router!\n");
|
|
nd->ndm_flags |= NTF_ROUTER;
|
|
}
|
|
#endif
|
|
|
|
/* AF_INET6 has a different value in VxWorks */
|
|
nd->ndm_family = wrnad_af_xlat(nd->ndm_family);
|
|
|
|
/* Change into request */
|
|
nlh->nlmsg_flags = NLM_F_REQUEST;
|
|
if (nlh->nlmsg_type == RTM_NEWNEIGH)
|
|
nlh->nlmsg_flags |= NLM_F_CREATE;
|
|
|
|
return 1;
|
|
}
|
|
|
|
#ifndef IPWRNAD
|
|
int wrnad_neigh_ndp_open(void)
|
|
{
|
|
int fd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
|
|
|
|
if (fd != -1)
|
|
{
|
|
struct icmp6_filter filter;
|
|
int val;
|
|
|
|
/* Setup filtering and ancillary data */
|
|
|
|
/* Block everything */
|
|
ICMP6_FILTER_SETBLOCKALL(&filter);
|
|
|
|
/* Standard neighbor advertisements */
|
|
ICMP6_FILTER_SETPASS(ND_NEIGHBOR_ADVERT, &filter);
|
|
|
|
/* Receive pktinfo */
|
|
val = 1;
|
|
if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &val, sizeof(val)) < 0)
|
|
{
|
|
wrnad_log(LOG_WARNING, "No IPv6 NDP unable to set RECVPKTINFO");
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
|
|
if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &val, sizeof(val)) < 0)
|
|
{
|
|
wrnad_log(LOG_WARNING, "No IPv6 NDP unable to set RECVHOPLIMIT");
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
|
|
/* Set the ICMP filter */
|
|
if (setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof(filter)) < 0)
|
|
{
|
|
wrnad_log(LOG_WARNING, "No IPv6 NDP unable to set FILTER");
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
|
|
return fd;
|
|
}
|
|
else
|
|
{
|
|
wrnad_log(LOG_WARNING, "No IPv6 NDP support possible");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static int wrnad_neigh_ndp_validate_options_len(struct nd_opt_hdr *first_opt, int tot_opt_len)
|
|
{
|
|
int opt_len;
|
|
struct nd_opt_hdr *opt = first_opt;
|
|
|
|
while (tot_opt_len > 0)
|
|
{
|
|
opt_len = opt->nd_opt_len;
|
|
tot_opt_len -= opt_len << 3;
|
|
if (opt_len == 0 || tot_opt_len < 0)
|
|
return 0;
|
|
opt = (struct nd_opt_hdr *) ((uint16_t*) opt + (opt_len << 2));
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static struct nd_opt_hdr *wrnad_neigh_ndp_get_option(struct nd_opt_hdr *first_opt, int tot_opt_len, uint16_t type)
|
|
{
|
|
int opt_len;
|
|
struct nd_opt_hdr *opt = first_opt;
|
|
|
|
while (tot_opt_len > 0)
|
|
{
|
|
opt_len = opt->nd_opt_len;
|
|
if (opt->nd_opt_type == type)
|
|
return opt;
|
|
tot_opt_len -= opt_len << 3;
|
|
opt = (struct nd_opt_hdr *) ((uint16_t*) opt + (opt_len << 2));
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static char recvaux[256];
|
|
void wrnad_neigh_ndp_read(int fd, int nlfd, char *recvbuf, int size)
|
|
{
|
|
struct cmsghdr *cmsghdr;
|
|
int len;
|
|
struct sockaddr_in6 src;
|
|
struct msghdr msg;
|
|
struct iovec vec;
|
|
struct nd_neighbor_advert *hdr = (struct nd_neighbor_advert *)recvbuf;
|
|
|
|
/* The read buffer */
|
|
vec.iov_base = recvbuf;
|
|
vec.iov_len = size;
|
|
|
|
/* Initialize name */
|
|
msg.msg_name = &src;
|
|
msg.msg_namelen = sizeof(src);
|
|
|
|
/* Initialize io */
|
|
msg.msg_iov = &vec;
|
|
msg.msg_iovlen = 1;
|
|
|
|
/* Initialize control */
|
|
msg.msg_control = recvaux;
|
|
msg.msg_controllen = 256;
|
|
|
|
/* Clear flags */
|
|
msg.msg_flags = 0;
|
|
|
|
if ((len = recvmsg(fd, &msg, MSG_DONTWAIT)) < 0)
|
|
{
|
|
wrnad_log(LOG_WARNING, "Read error on ICMPv6 NDP socket");
|
|
return;
|
|
}
|
|
|
|
/* Check 5 */
|
|
if (len < sizeof(*hdr))
|
|
{
|
|
wrnad_log(LOG_WARNING, "Read on ICMPv6 NDP socket produced a too short frame");
|
|
return;
|
|
}
|
|
|
|
if ((msg.msg_flags & (MSG_TRUNC | MSG_CTRUNC)) != 0)
|
|
{
|
|
wrnad_log(LOG_WARNING, "Read on ICMPv6 NDP socket was truncated");
|
|
return;
|
|
}
|
|
|
|
/* Received one solicitation message */
|
|
|
|
/* Did we get any ancillary data? */
|
|
if (msg.msg_controllen > 0)
|
|
{
|
|
struct in6_pktinfo *pktinfo = NULL;
|
|
int hoplimit = 0;
|
|
Wrnad_proxy_interface *ifp;
|
|
struct nd_opt_hdr *opt;
|
|
|
|
/* Loop the ancillary data, see what we find */
|
|
for (cmsghdr = CMSG_FIRSTHDR(&msg); cmsghdr != 0; cmsghdr = CMSG_NXTHDR(&msg, cmsghdr))
|
|
{
|
|
/* Correct level? */
|
|
if (cmsghdr->cmsg_level == IPPROTO_IPV6)
|
|
{
|
|
/* Type? */
|
|
switch (cmsghdr->cmsg_type)
|
|
{
|
|
case IPV6_PKTINFO:
|
|
pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsghdr);
|
|
break;
|
|
|
|
case IPV6_HOPLIMIT:
|
|
/* Got some destination options */
|
|
hoplimit = *((int *)CMSG_DATA(cmsghdr));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (NULL == pktinfo)
|
|
{
|
|
wrnad_log(LOG_WARNING, "Read on ICMPv6 NDP socket produced no packet information");
|
|
return;
|
|
}
|
|
|
|
/* OK, first sanities passed */
|
|
|
|
/* RFC 2461
|
|
7.1.2. Validation of Neighbor Advertisements
|
|
|
|
A node MUST silently discard any received Neighbor Advertisement
|
|
messages that do not satisfy all of the following validity checks:
|
|
|
|
1. The IP Hop Limit field has a value of 255, i.e., the packet
|
|
could not possibly have been forwarded by a router.
|
|
|
|
2. If the message includes an IP Authentication Header, the message
|
|
authenticates correctly.
|
|
|
|
3. ICMP Checksum is valid.
|
|
|
|
4. ICMP Code is 0.
|
|
|
|
5. ICMP length (derived from the IP length) is 24 or more octets.
|
|
|
|
6. Target Address is not a multicast address.
|
|
|
|
7. If the IP Destination Address is a multicast address the
|
|
Solicited flag is zero.
|
|
|
|
8. All included options have a length that is greater than zero.
|
|
*/
|
|
|
|
/* Check 1 */
|
|
if (hoplimit != 0xFF)
|
|
{
|
|
wrnad_log(LOG_WARNING, "Read on ICMPv6 NA had illegal hoplimit");
|
|
return;
|
|
}
|
|
|
|
/* Check 2 is done by Linux */
|
|
/* Check 3 is done by Linux */
|
|
|
|
/* Check 4 */
|
|
if (hdr->nd_na_hdr.icmp6_code != 0)
|
|
{
|
|
wrnad_log(LOG_WARNING, "Read on ICMPv6 NDP frame had bad icmp6 code");
|
|
return;
|
|
}
|
|
|
|
/* Check 5 is done immediately after recvmsg */
|
|
|
|
|
|
/* Check 6 */
|
|
if (IN6_IS_ADDR_MULTICAST(&hdr->nd_na_target))
|
|
{
|
|
wrnad_log(LOG_DEBUG, "Read on ICMPv6 NDP frame had multicast target");
|
|
return;
|
|
}
|
|
|
|
/* Check 7 */
|
|
if (IN6_IS_ADDR_MULTICAST(&pktinfo->ipi6_addr))
|
|
{
|
|
/* Dealt with by Linux - We do not do unsolicited advertisements here */
|
|
wrnad_log(LOG_DEBUG, "Read on ICMPv6 NDP frame had multicast destination");
|
|
return;
|
|
}
|
|
|
|
/* Check 8 */
|
|
if (0 == wrnad_neigh_ndp_validate_options_len((struct nd_opt_hdr *) (hdr + 1), len - sizeof(*hdr)))
|
|
{
|
|
wrnad_log(LOG_WARNING, "Read on ICMPv6 NDP frame had bad icmp6 options");
|
|
return;
|
|
}
|
|
|
|
/* Retrieve the link layer address */
|
|
opt = wrnad_neigh_ndp_get_option((struct nd_opt_hdr *) (hdr + 1), len - sizeof(*hdr), ND_OPT_TARGET_LINKADDR);
|
|
if (NULL == opt)
|
|
{
|
|
wrnad_log(LOG_DEBUG, "Read on ICMPv6 NDP frame had bad no target link address");
|
|
return;
|
|
}
|
|
|
|
|
|
ifp = wrnad_rtnl_check_proxy_interface(pktinfo->ipi6_ifindex);
|
|
if (NULL == ifp)
|
|
{
|
|
wrnad_log(LOG_DEBUG, "Read on ICMPv6 NDP frame correct, but received on Non-Proxy interface");
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
struct nlmsghdr *nlh;
|
|
struct ndmsg *ndm;
|
|
struct rtattr *rta;
|
|
unsigned int ll_size;
|
|
struct sockaddr_nl nladdr;
|
|
static int seq = 0;
|
|
|
|
|
|
/* Create the NETLINK header */
|
|
nlh = (struct nlmsghdr *)recvaux;
|
|
memset(nlh, 0, sizeof(*nlh));
|
|
nlh->nlmsg_flags = NLM_F_EXCL|NLM_F_CREATE|NLM_F_REQUEST;
|
|
nlh->nlmsg_type = RTM_NEWNEIGH;
|
|
nlh->nlmsg_seq = ++seq;
|
|
nlh->nlmsg_len = NLMSG_LENGTH(0);
|
|
|
|
/* Create the NDM header */
|
|
ndm = (struct ndmsg *)((char *)nlh + nlh->nlmsg_len);
|
|
memset(ndm, 0, sizeof(*ndm));
|
|
ndm->ndm_family = AF_INET6;
|
|
ndm->ndm_flags = 0;
|
|
ndm->ndm_ifindex = ifp->ifindex;
|
|
ndm->ndm_state = NUD_STALE;
|
|
ndm->ndm_type = 0;
|
|
|
|
/* move past the NDM header */
|
|
nlh->nlmsg_len += sizeof(*ndm);
|
|
|
|
/* Add the DST address */
|
|
rta = (struct rtattr *)((char *)nlh + nlh->nlmsg_len);
|
|
rta->rta_len = RTA_LENGTH(sizeof(struct in6_addr));
|
|
rta->rta_type = NDA_DST;
|
|
memcpy(RTA_DATA(rta), &hdr->nd_na_target, sizeof(struct in6_addr));
|
|
|
|
/* Increase */
|
|
nlh->nlmsg_len += NLMSG_ALIGN(rta->rta_len);
|
|
|
|
/* Add the link layer address option */
|
|
ll_size = opt->nd_opt_len << 3;
|
|
ll_size -= sizeof(*opt);
|
|
|
|
rta = (struct rtattr *)((char *)nlh + nlh->nlmsg_len);
|
|
rta->rta_len = RTA_LENGTH(ll_size);
|
|
rta->rta_type = NDA_LLADDR;
|
|
memcpy(RTA_DATA(rta), opt + 1, ll_size);
|
|
|
|
/* Increase */
|
|
nlh->nlmsg_len += NLMSG_ALIGN(rta->rta_len);
|
|
|
|
memset(&msg, 0, sizeof(msg));
|
|
memset(&nladdr, 0, sizeof(nladdr));
|
|
|
|
/* Setup the sockaddress destination structure */
|
|
nladdr.nl_family = AF_NETLINK;
|
|
|
|
/* Setup the vector */
|
|
vec.iov_base = nlh;
|
|
vec.iov_len = nlh->nlmsg_len;
|
|
|
|
/* Initiate the msg structure */
|
|
msg.msg_name = &nladdr;
|
|
msg.msg_namelen = sizeof(nladdr);
|
|
msg.msg_iov = &vec;
|
|
msg.msg_iovlen = 1;
|
|
|
|
/* Talk with the kernel */
|
|
if (sendmsg(nlfd, &msg, 0) < 0)
|
|
{
|
|
/* Failed to communicate */
|
|
wrnad_log(LOG_WARNING, "Read on ICMPv6 NDP frame failed to send");
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/**/
|
|
wrnad_log(LOG_WARNING, "Read on ICMPv6 NDP socket produced no ancillary data?");
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|