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