[ds6-devel] NDP
Chad N. Tindel
ctindel at falcon.csc.calpoly.edu
Thu Mar 27 10:43:50 CET 2003
Hello all-
I'm trying to write a program which allows a user to send unsolicited
neighbor advertisements, and I've been running up against a brick wall for
the last day or so. I've got a program which hard codes some values for
simplicity, so here's the setup that I'm working with.
I've got two computers, machine A with address fec0:0:0:1::1 and machine B
with address fec0:0:0:1::2. Machine B has an link-layer address of
00:01:02:75:62:ad.
I've entered a fake address into machine A's NDP cache, so it looks like
this:
[root at ct742301 arping6]# ip -6 neigh show
fec0:0:0:1::2 dev eth0 lladdr 00:01:02:75:00:43 nud permanent
and I'm attempting to generate a neighbor advertisement coming from
machine B to put in the correct link layer address into Machine B's cache.
Below is the source code that is used to do this. If anybody sees
anything wrong with this, your eyes would be most helpful!
Also, I'm not sure if there would be a use for this in some sort of
toolkit. Originally the code was based on rtsol.c, but it doesn't much
resemble that anymore. Would anybody else find this useful?
It should compile cleanly on linux with
gcc -Wall -Werror -g -o arping6 arping6.c
Thanks in advance,
Chad
P.S. My mail from this list is really messed up. Things that were sent
out on Tuesday didn't get back to me until Thursday, and it came all out
of order. I don't know exactly where the problem is, and it might be on
my side, but I was wondering if anybody else has been experiencing this at
all?
----------------------------------------------------------------
#include <sys/param.h>
#include <sys/socket.h>
#ifdef ADVAPI
#include <sys/uio.h>
#endif
#include <sys/ioctl.h>
#include <net/if.h>
#include <time.h>
#if defined(__FreeBSD__) && __FreeBSD__ >= 3
#include <net/if_var.h>
#endif /* __FreeBSD__ >= 3 */
#include <netinet/in.h>
#include <netinet/ip6.h>
/*#include <netinet/icmp6.h>*/
#include <unistd.h>
#include <stdio.h>
#include <err.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/sysctl.h>
#include <netdb.h>
/*#include <linux/ipv6.h>*/
#include <linux/icmpv6.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <arpa/inet.h>
/*#include <netinet6/in6_var.h>*/
/*#include <netinet6/nd6.h>*/
#include <asm/byteorder.h>
#define ALLNEIGHBOR "FF01::1"
#define ND_OPT_TARGET_LL_ADDR 2
#define ND_NEIGHBOR_ADVERT 136
#define ARPHRD_ETHER 1
int main __P((int, char **, char **));
void usage __P((void));
int sendit __P((int, char *, int, struct sockaddr *, int, int));
int interface_down __P((char *));
int interface_up __P((char *));
static int trick = 1;
static int verbose = 0;
extern char *optarg;
extern int optind;
struct ipv6hdr {
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u8 priority:4,
version:4;
#elif defined(__BIG_ENDIAN_BITFIELD)
__u8 version:4,
priority:4;
#else
#error "Please fix <asm/byteorder.h>"
#endif
__u8 flow_lbl[3];
__u16 payload_len;
__u8 nexthdr;
__u8 hop_limit;
struct in6_addr saddr;
struct in6_addr daddr;
};
struct nd_msg {
struct ipv6hdr ip6h;
struct icmp6hdr icmph;
struct in6_addr target;
unsigned char opt_header[2];
unsigned char opt_lladdr[6];
};
typedef unsigned short sg_addr6_t[8];
#define SG_IN6_ADDR_COPY(DST_IP, SRC_IP) \
memcpy((void *)(DST_IP), (void *)(SRC_IP), sizeof(sg_addr6_t))
int
main(int argc, char *argv[], char *envp[])
{
struct sockaddr_in6 to;
struct ifreq ifr;
u_int hlim = 255;
u_short index;
unsigned char hw_addr[IFHWADDRLEN];
int hw_addr_length;
int s, i;
int opt;
struct nd_msg msg;
int ret_val;
int if_index;
int msg_len = sizeof(struct nd_msg);
char addr6_buf[512];
struct in6_addr target_addr6;
struct sockaddr_ll dev_addr;
while ((opt = getopt(argc, argv, "nv")) != -1) {
switch (opt) {
case 'n':
trick = 0;
break;
case 'v':
verbose++;
break;
default:
usage();
}
}
argc -= optind;
argv += optind;
if (argc != 1) {
usage();
}
index = (u_short)if_nametoindex(argv[0]);
if (index == 0) {
printf("invalid interface %s\n", argv[0]);
return -1;
}
printf("Index from if_nametoindex(%s) is %d\n", argv[0], index);
{
struct addrinfo hints, *res;
int error;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET6;
error = getaddrinfo(ALLNEIGHBOR, NULL, &hints, &res);
if (error) {
perror("getaddrinfo failed");
}
memcpy(&to, res->ai_addr, res->ai_addrlen);
printf("Destination address is %s\n",
inet_ntop(AF_INET6, (void*)&to.sin6_addr, addr6_buf,
sizeof(struct in6_addr)));
}
memset(&target_addr6, 0, sizeof(target_addr6));
target_addr6.s6_addr16[0] = ntohs(0xfec0);
target_addr6.s6_addr16[1] = ntohs(0x0);
target_addr6.s6_addr16[2] = ntohs(0x0);
target_addr6.s6_addr16[3] = ntohs(0x1);
target_addr6.s6_addr16[7] = ntohs(0x2);
printf("Target address is %s\n",
inet_ntop(AF_INET6, (void*)&target_addr6, addr6_buf,
sizeof(struct in6_addr)));
SG_IN6_ADDR_COPY(&msg.target, &target_addr6);
if ((s = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IPV6))) < 0)
/*if ((s = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL))) < 0)*/
err(1, "socket");
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, argv[0], IFNAMSIZ);
if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
ret_val = errno;
printf("SIOCGIFINDEX failed with error %s.\n", strerror(errno));
close(s);
return(ret_val);
}
if_index = ifr.ifr_ifindex;
printf("if_index of %s is %d\n", argv[0], if_index);
if (ioctl(s, SIOCGIFHWADDR, &ifr) < 0) {
ret_val = errno;
printf("Failed to get hardware address of interface %s: %s\n",
argv[0], strerror(errno));
close(s);
return(ret_val);
}
memcpy(hw_addr, ifr.ifr_hwaddr.sa_data, IFHWADDRLEN);
hw_addr_length = IFHWADDRLEN;
printf("Hardware addr is: ");
for (i = 0; i < hw_addr_length; i++) {
if ((i + 1) < hw_addr_length) {
printf("%02x:", hw_addr[i]);
} else {
printf("%02x\n", hw_addr[i]);
}
}
memset(&msg, 0, sizeof(msg));
/* fill Ethernet header */
dev_addr.sll_family = AF_PACKET;
dev_addr.sll_protocol = htons(ETH_P_IPV6);
dev_addr.sll_ifindex = if_index;
dev_addr.sll_hatype = htons(ARPHRD_ETHER);
dev_addr.sll_pkttype = htons(PACKET_BROADCAST);
dev_addr.sll_halen = hw_addr_length;
memset(dev_addr.sll_addr, 0xff, dev_addr.sll_halen);
/* fill IPv6 header */
msg.ip6h.version = 6;
msg.ip6h.payload_len = htons(32);
msg.ip6h.nexthdr = 0x3a; /* ICMPv6 */
msg.ip6h.hop_limit = hlim;
SG_IN6_ADDR_COPY(&msg.ip6h.saddr, &target_addr6);
SG_IN6_ADDR_COPY(&msg.ip6h.daddr, &to.sin6_addr);
/* fill ICMPv6 header */
msg.icmph.icmp6_type = ND_NEIGHBOR_ADVERT;
msg.icmph.icmp6_code = 0;
msg.icmph.icmp6_cksum = 0xf5f3;
msg.icmph.icmp6_unused = 0;
msg.icmph.icmp6_router = 0;
msg.icmph.icmp6_solicited = 0; /* This is an unsolicited advertisement */
msg.icmph.icmp6_override = 1; /* Override any exist NDP cache entries */
SG_IN6_ADDR_COPY(msg.target.s6_addr16, &target_addr6);
/* fill ICMP Options */
msg.opt_header[0] = 2; /* Option type two, link level address */
msg.opt_header[1] = 1; /* Option length is one*8 bytes*/
memcpy(&msg.opt_lladdr, hw_addr, hw_addr_length);
/*
* Bind the raw socket to the device so that we can only send
* or receive packets to and from this particular interface.
*/
if (bind(s, (struct sockaddr *)&dev_addr, sizeof(dev_addr)) < 0) {
ret_val = errno;
printf("Binding raw socket to %s failed with error %s.\n",
argv[0], strerror(errno));
close(s);
return(ret_val);
}
i = sendit(s, (char *)&msg, msg_len, (struct sockaddr *)&dev_addr,
sizeof(dev_addr), index);
printf("Sent %d bytes\n", i);
if (i < 0) {
perror("Error with sendto");
} else if (i != msg_len) {
fprintf(stderr, "arping6: short write (wrote %d of be %d)\n",
i, msg_len);
}
close(s);
exit(0);
}
void
usage()
{
(void)fprintf(stderr, "usage: arping6 [-nv] ifname\n");
exit(1);
}
int
sendit(s, p, len, sock, sock_len, ifindex)
int s;
char *p;
int len;
struct sockaddr *sock;
int ifindex;
{
return sendto(s, p, len, 0, (struct sockaddr *)sock, sock_len);
}
More information about the ds6-devel
mailing list