NDP(4P) Protocols NDP(4P)
NAME
ndp,
NDP - Neighbor Discovery Protocol
SYNOPSIS
#include <sys/socket.h> #include <sys/sockio.h> #include <netinet/in.h> #include <net/if.h> s = socket(PF_INET6, SOCK_DGRAM, 0);
struct lifreq lifr;
ioctl(s, SIOCLIFGETND, &lifr);
ioctl(s, SIOCLIFSETND, &lifr);
ioctl(s, SIOCLIFDELND, &lifr);
DESCRIPTION
The Neighbor Discovery Protocol (NDP) is a protocol used to distribute
and request information about neighboring IPv6 systems on the local
network, much like
ARP(4P) for IPv4. NDP is also responsible for
spreading information about the network gateway and how hosts should
configure themselves (see
in.ndpd(8) for more on how this happens).
APPLICATION PROGRAMMING INTERFACE
The operating system provides several ioctls to help manipulate the
mappings obtained through NDP. They are
SIOCLIFGETND,
SIOCLIFSETND,
and
SIOCLIFDELND, for getting, setting, and deleting respectively.
Each of these ioctls takes a
struct lifreq (see
if(4P) for details),
where the
lifr_lifru field is of type
struct lif_nd_req:
typedef struct lif_nd_req {
struct sockaddr_storage lnr_addr;
uint8_t lnr_state_create;
uint8_t lnr_state_same_lla;
uint8_t lnr_state_diff_lla;
int lnr_hdw_len;
int lnr_flags;
int lnr_pad0;
char lnr_hdw_addr[ND_MAX_HDW_LEN];
} lif_nd_req_t;
The
lnr_addr field should be filled in with an IPv6 address (see
sockaddr_in6(3SOCKET)), and the
lnr_hdw_addr is the link-layer address
of length
lnr_hdw_len.
State flags for
lnr_state_create,
lnr_state_same_lla, and
lnr_state_diff_lla can be set to one of the following values:
ND_UNCHANGED For ioctls that don't modify state
ND_INCOMPLETE Address resolution is currently in progress
ND_REACHABLE The link-layer address has recently been
reachable
ND_STALE The link-layer address may be unreachable, and
the system shouldn't do anything
ND_DELAY This entry hasn't yet started sending Neighbor
Solicitations
ND_PROBE The operating system is currently sending out
Neighbor Solicitations for the address
ND_UNREACHABLE The link-layer address is unreachable, and this
entry is going to be deleted.
When creating a new entry, the only valid values for
lnr_state_create are
ND_REACHABLE and
ND_STALE. Any other value will return
EINVAL.
The
lnr_state_same_lla and
lnr_state_diff_lla fields are reserved for
future use and can be safely set to
ND_UNCHANGED and
ND_STALE respectively.
Flags that can be placed in
lnr_flags are:
NDF_ISROUTER_ON Mark this entry as being a router. This will
cause Neighbor Advertisements for this address
to be sent with the R-bit (Router).
NDF_ISROUTER_OFF If this entry was flagged as being a router,
remove the flag.
NDF_ANYCAST_ON Mark this entry as being for an anycast
address. This prevents sending Neighbor
Advertisements with the O-bit (Override).
NDF_ANYCAST_OFF If this entry was flagged as an anycast
address, remove the flag.
NDF_STATIC Prevent this entry from being deleted by the
system.
When using
SIOCLIFGETND, these flags represent the current state of the
corresponding Neighbor Cache Entry. When using
SIOCLIFSETND, these
flags represent what changes should be applied to the underlying entry.
The only fields that need to be set for the
SIOCLIFGETND or
SIOCLIFDELND ioctls are
lifr_name and
lnr_addr. All other fields
should be zeroed out. After successfully getting an entry, the other
fields will be filled in. When using
SIOCLIFSETND, all fields should
be set to an appropriate value, as described above, with the exception
of
lnr_pad0, which is unused and only exists for padding purposes.
After performing the ioctl, the following errors may be returned
through the global
errno variable:
EAFNOSUPPORT A non-IPv6 socket was used to perform the
ioctl.
EINVAL The request contents were bad. This could be
because conflicting flags were used, the
specified interface wasn't logical unit zero,
or another reason.
ENOMEM The system ran out of memory for internal data
structures.
ENXIO The specified interface does not exist.
EPERM The caller does not have permission to modify
the Neighbor Cache Entries associated with this
interface. They may be lacking the
PRIV_SYS_NET_CONFIG privilege (see
privileges(7)), or the interface is managed by
IPMP (IP Network Multipathing).
ESRCH There is no entry matching the specified
address.
EXAMPLES
The following examples demonstrate how to get and set NDP mappings
using the provided ioctls. They can be compiled by using a C compiler
and linking against the sockets library.
Example 1: Getting a mapping $ gcc -Wall -lsocket -o get get.c
$ cat get.c
/*
* Example of getting a mapping for a node name.
*/
#include <strings.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <unistd.h>
#include <netdb.h>
#include <net/if.h>
int get(char *host) {
struct lifreq lifr;
struct addrinfo hints, *serverinfo, *p;
int err, s;
bzero(&hints, sizeof (struct addrinfo));
hints.ai_family = PF_INET6;
hints.ai_protocol = IPPROTO_IPV6;
if ((err = getaddrinfo(host, NULL, &hints, &serverinfo)) != 0) {
(void) fprintf(stderr, "Unable to lookup %s: %s\n", host,
gai_strerror(err));
return (1);
}
s = socket(AF_INET6, SOCK_DGRAM, 0);
if (s < 0) {
perror("Failed to open IPv6 socket");
return (1);
}
for (p = serverinfo; p != NULL; p = p->ai_next) {
/* Zero out structure */
bzero(&lifr, sizeof (struct lifreq));
(void) strlcpy(lifr.lifr_name, "net0",
sizeof (lifr.lifr_name));
(void) memcpy(&lifr.lifr_nd.lnr_addr, p->ai_addr,
sizeof (struct sockaddr_storage));
/* Get mapping */
if (ioctl(s, SIOCLIFGETND, &lifr) < 0) {
perror("Unable to get NDP mapping");
continue;
}
/*
* lifr.lifr_nd.lnr_hdw_addr now contains the MAC address,
* and can be used as desired.
*/
}
/*
* Clean up linked list.
*/
freeaddrinfo(serverinfo);
return (0);
}
int main(int argc, char *argv[]) {
if (argc < 2)
exit(1);
return (get(argv[1]));
}
Deleting a mapping would work similarly, except that instead of using
SIOCLIFGETND, you would instead use the
SIOCLIFDELND ioctl.
Example 2: Adding a mapping $ gcc -Wall -lsocket -o set set.c
$ cat set.c
/*
* Example of setting a mapping to an all-zero Ethernet address.
*/
#include <strings.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <unistd.h>
#include <netdb.h>
#include <net/if.h>
int set(char *host) {
struct lifreq lifr;
struct addrinfo hints, *serverinfo, *p;
int err, s;
bzero(&hints, sizeof (struct addrinfo));
hints.ai_family = PF_INET6;
hints.ai_protocol = IPPROTO_IPV6;
if ((err = getaddrinfo(host, NULL, &hints, &serverinfo)) != 0) {
(void) fprintf(stderr, "Unable to lookup %s: %s\n", host,
gai_strerror(err));
return (1);
}
s = socket(AF_INET6, SOCK_DGRAM, 0);
if (s < 0) {
perror("Failed to open IPv6 socket");
return (1);
}
for (p = serverinfo; p != NULL; p = p->ai_next) {
/* Zero out structure */
bzero(&lifr, sizeof (struct lifreq));
(void) strlcpy(lifr.lifr_name, "net0",
sizeof (lifr.lifr_name));
(void) memcpy(&lifr.lifr_nd.lnr_addr, p->ai_addr,
sizeof (struct sockaddr_storage));
lifr.lifr_nd.lnr_state_create = ND_REACHABLE;
lifr.lifr_nd.lnr_flags = NDF_STATIC;
/* Get mapping */
if (ioctl(s, SIOCLIFSETND, &lifr) < 0) {
perror("Unable to set NDP mapping");
continue;
}
}
/*
* Clean up linked list.
*/
freeaddrinfo(serverinfo);
return (0);
}
int main(int argc, char *argv[]) {
if (argc < 2)
exit(1);
return (set(argv[1]));
}
SEE ALSO
sockaddr_in6(3SOCKET),
privileges(7),
ifconfig(8),
in.ndpd(8),
ndp(8) Narten, T., Nordmark, E., Simpson, W., and Soliman, H.,
RFC 4861,
Neighbor Discovery for IP version 6, September 2007.
illumos December 2, 2023 illumos