#include #include #include #include #include #include #include #include #ifdef __linux__ #define HAVE_IPV6_IPPROTO_RAW 1 #endif #define ETH_DEV "en0" #define ETH_NULL "\0\0\0\0\0\0" void hexdump(const unsigned char *data, unsigned int n) { const unsigned char *p; p = data; while (p - data < n) { unsigned int i; for (i = 0; p - data + i < n && i < 8; i++) printf(" %02X", p[i]); if (p - data < n) printf(" "); for (; p - data + i < n && i < 16; i++) printf(" %02X", p[i]); printf("\n"); p += i; } } int handler(const struct intf_entry *intf, void *arg) { int i; printf("%s %s\n", intf->intf_name, addr_ntoa(&intf->intf_link_addr)); printf(" %s\n", addr_ntoa(&intf->intf_addr)); for (i = 0; i < intf->intf_alias_num; i++) printf(" %s\n", addr_ntoa(&intf->intf_alias_addrs[i])); return 0; } int print_interfaces(void) { intf_t *intf_handle; intf_handle = intf_open(); if (intf_handle == NULL) return -1; if (intf_loop(intf_handle, handler, NULL) == -1) return -1; return 0; } /* There are three ways to send a raw IPv6 packet. send_ipv6_eth works when the device is Ethernet. (Unfortunately IPv6-in-IPv4 tunnels are not.) We can control all header fields and extension headers. send_ipv6_ipproto_raw must be used when IPPROTO_RAW sockets include the IP header, like IP_HDRINCL for IPv4. This is non-standard but is the case on Linux. (On other platforms, IPPROTO_RAW has no special meaning and just stands for protocol 255.) We can control all header fields and extension headers. This method uses only one raw socket for all sends. send_ipv6_ip must be used when IPPROTO_RAW sockets do not include the IP header. Through standard function calls we can control all header fields except for the flow label. This method needs one raw socket for every protocol. (More precisely, one socket per distinct Next Header value.) */ int send_ipv6_eth(const unsigned char *packet, size_t len) { struct eth_hdr *eth_hdr; char *copy; eth_t *eth; int n; copy = NULL; n = -1; copy = malloc(len + sizeof(*eth_hdr)); if (copy == NULL) return -1; memcpy(copy + sizeof(*eth_hdr), packet, len); eth_hdr = (struct eth_hdr *) copy; eth_pack_hdr(eth_hdr, ETH_NULL, ETH_NULL, ETH_TYPE_IPV6); eth = eth_open(ETH_DEV); if (eth == NULL) goto bail; n = eth_send(eth, copy, sizeof(*eth_hdr) + len); bail: free(copy); eth_close(eth); return n; } int send_ipv6_ipproto_raw(const unsigned char *packet, size_t len) { struct ip6_hdr *hdr; struct sockaddr_in6 dest = { 0 }; int sd, n; sd = -1; n = -1; if (len < sizeof(*hdr)) return -1; hdr = (struct ip6_hdr *) packet; dest.sin6_family = AF_INET6; memcpy(&dest.sin6_addr.s6_addr, &hdr->ip6_dst, sizeof(dest.sin6_addr.s6_addr)); dest.sin6_port = 0; sd = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW); if (sd == -1) { perror("socket"); goto bail; } n = sendto(sd, packet, len, 0, (struct sockaddr *) &dest, sizeof(dest)); if (n == -1) perror("sendmsg"); bail: if (sd != -1) close(sd); return n; } /* Add an ancillary cmsghdr data block to the list of blocks in a msghdr. The list is stored in msg->msg_control, which is must be allocated to hold at least maxlen bytes. msg->msg_controllen is also modified by this function. Returns -1 in case of error or 0 otherwise. */ static int add_ancillary(struct msghdr *msg, size_t maxlen, int level, int type, const void *data, size_t len) { struct cmsghdr *cm; if (maxlen < msg->msg_controllen + CMSG_SPACE(len)) return -1; cm = (struct cmsghdr *) ((char *) msg->msg_control + msg->msg_controllen); msg->msg_controllen += CMSG_SPACE(len); cm->cmsg_len = CMSG_LEN(len); cm->cmsg_level = level; cm->cmsg_type = type; memcpy(CMSG_DATA(cm), data, len); return 0; } int send_ipv6_ip(const unsigned char *packet, size_t len) { struct msghdr msg; struct sockaddr_in6 dest = { 0 }; struct iovec iov; /* Allocate a control buffer big enough to hold the IPV6_TCLASS and IPV6_HOPLIMIT options. This is a byte buffer but must be aligned for a struct cmsghdr. See section 15.7 (p. 425) of Unix Network Programming, third edition. */ union { struct cmsghdr dummy_align; char control[CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(int))]; } control_un; struct ip6_hdr *hdr; int tclass, hoplimit; int sd; int n; sd = -1; n = -1; /* Set up sendmsg data structure. dest and iov are filled in below. */ msg.msg_name = &dest; msg.msg_namelen = sizeof(dest); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = &control_un.control; msg.msg_controllen = 0; msg.msg_flags = 0; if (len < sizeof(*hdr)) return -1; hdr = (struct ip6_hdr *) packet; dest.sin6_family = AF_INET6; memcpy(&dest.sin6_addr.s6_addr, &hdr->ip6_dst, sizeof(dest.sin6_addr.s6_addr)); dest.sin6_port = 0; iov.iov_base = (unsigned char *) packet + sizeof(*hdr); iov.iov_len = len - sizeof(*hdr); /* This can also be set with setsockopt(IPPROTO_IPV6, IPV6_TCLASS). */ tclass = ntohl(hdr->ip6_flow & IP6_FLOWINFO_MASK) >> 20; if (add_ancillary(&msg, sizeof(control_un.control), IPPROTO_IPV6, IPV6_TCLASS, &tclass, sizeof(tclass)) == -1) { goto bail; } /* This can also be set with setsockopt(IPPROTO_IPV6, IPV6_UNICAST_HOPS). */ hoplimit = hdr->ip6_hlim; if (add_ancillary(&msg, sizeof(control_un.control), IPPROTO_IPV6, IPV6_HOPLIMIT, &hoplimit, sizeof(hoplimit)) == -1) { goto bail; } /* The Next Header field is set when the socket is created. The payload length is set in the call to sendmsg. There's no way to set the flow label. */ sd = socket(AF_INET6, SOCK_RAW, hdr->ip6_nxt); if (sd == -1) { perror("socket"); goto bail; } n = sendmsg(sd, &msg, 0); if (n == -1) perror("sendmsg"); bail: if (sd != -1) close(sd); return n; } int send_ipv6(const unsigned char *packet, size_t len) { #if HAVE_IPV6_IPPROTO_RAW return send_ipv6_ipproto_raw(packet, len); #endif #ifdef ETH_DEV return send_ipv6_eth(packet, len); #endif return send_ipv6_ip(packet, len); } int main() { unsigned char packet[100]; struct ip6_hdr *ip6_hdr; const char src[] = IP6_ADDR_LOOPBACK; const char dst[] = "\x12\x34\x56\x78\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02"; int res; print_interfaces(); memset(packet, 0xa5, sizeof(packet)); ip6_hdr = (struct ip6_hdr *) packet; ip6_pack_hdr(ip6_hdr, 0x12, 0xabcde, sizeof(packet) - sizeof(*ip6_hdr), IP_PROTO_HOPOPTS, 13, src[0], dst[0]); packet[40] = IP_PROTO_FRAGMENT; packet[41] = 0; packet[42] = 0; packet[43] = 0; packet[44] = 0; packet[45] = 0; packet[46] = 0; packet[47] = 0; packet[48] = IP_PROTO_DSTOPTS; packet[49] = 0; packet[50] = 0; packet[51] = 0; packet[52] = 0; packet[53] = 0; packet[54] = 0; packet[55] = 0; packet[56] = IP_PROTO_NONE; packet[57] = 0; packet[58] = 0; packet[59] = 0; packet[60] = 0; packet[61] = 0; packet[62] = 0; packet[63] = 0; res = send_ipv6_eth(packet, sizeof(packet)); if (res == -1) perror("send_ipv6_eth"); res = send_ipv6_ipproto_raw(packet, sizeof(packet)); if (res == -1) perror("send_ipv6_ipproto_raw"); res = send_ipv6_ip(packet, sizeof(packet)); if (res == -1) perror("send_ipv6_ip"); res = send_ipv6(packet, sizeof(packet)); if (res == -1) perror("send_ipv6"); return 0; }