diff options
Diffstat (limited to 'mtu1280d.c')
-rw-r--r-- | mtu1280d.c | 525 |
1 files changed, 525 insertions, 0 deletions
diff --git a/mtu1280d.c b/mtu1280d.c new file mode 100644 index 0000000..7948af9 --- /dev/null +++ b/mtu1280d.c @@ -0,0 +1,525 @@ +// Thanks to Austin Marton +// https://austinmarton.wordpress.com/2011/09/14/sending-raw-ethernet-packets-from-a-specific-interface-in-c-on-linux/ +// csum() is borrowed from Austin; and csum_3() is derived from csum(). + +// Portions of this file derived from libnetfilter_queue-1.0.2/utils/nfqnl_test.c +// (C) 2005 by Harald Welte <laforge@gnumonks.org> +// Particularly the bits that interface with netfilter (and the trigger for this being GPLv2 instead of MIT license) + +// Code not otherwise borrowed is +// (C) 2015 by Jason Fesler <jfesler@gigo.com> +// Principally: anything to do with ICMPv6 responses +// The uglier it looks, the more likely it is mine. + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <assert.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <net/if.h> +#include <netinet/ip.h> +#include <netinet/udp.h> +#include <netinet/ether.h> +#include <linux/if_packet.h> +#include <linux/sockios.h> + +// netfilter - lots of stuff pilfered from nfqnl_test.c +#include <linux/netfilter.h> /* for NF_ACCEPT */ +#include <libnetfilter_queue/libnetfilter_queue.h> + +#define MTU 1280 +#define ETHER_SIZE (6+6+2) +#define IPV6HDR_SIZE 40 +#define ICMP6_SIZE 8 +#define PAYLOAD_SIZE (MTU-(IPV6HDR_SIZE+ICMP6_SIZE)) +#define ETHER_CRC_SIZE 4 +#define ETHER_TOTAL_SIZE (MTU + ETHER_SIZE) + +typedef struct fullframe +{ + u_int8_t ether_frame[ETHER_SIZE]; + u_int8_t ipv6_header[IPV6HDR_SIZE]; + u_int8_t icmp6_header[ICMP6_SIZE]; + u_int8_t payload[PAYLOAD_SIZE]; +} fullframe; + +int +sockfd (void) +{ + static sock = 0; + if (!sock) + { + sock = socket (AF_PACKET, SOCK_RAW, IPPROTO_RAW); + } + if (sock == -1) + { + perror ("socket"); + } +} + +uint8_t * +macaddr_for_interface (int i) +{ + static int last_i = 0xfffff; + static uint8_t buffer[6]; + static uint8_t devname[IF_NAMESIZE]; + + if (i != last_i) + { + int s = sockfd (); // * Need a random socket FD to do ioctl against + char *interface = NULL; + memset (buffer, 0, sizeof (buffer)); + interface = if_indextoname (i, devname); + struct ifreq ifr; + + if (interface) + { + printf ("Looked up %d, found %s ", i, interface); + + // Use ioctl() to look up interface name and get its MAC address. + memset (&ifr, 0, sizeof (ifr)); + snprintf (ifr.ifr_name, sizeof (ifr.ifr_name), "%s", interface); + if (ioctl (s, SIOCGIFHWADDR, &ifr) < 0) + { + perror ("ioctl() failed to get source MAC address "); + exit (1); + } + memcpy (buffer, ifr.ifr_hwaddr.sa_data, 6); + + } + + } + printf ("interface %d mac %02x:%02x:%02x:%02x:%02x:%02x", + i, buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], + buffer[5]); + + return buffer; +} + + +void +hexdump (char *s, uint8_t * p, int n) +{ + int i; + printf ("\nHEXDUMP: %s\n", s); + for (i = 0; i < n; i++) + { + if (i % 16 == 0) + { + printf ("%04x: ", i); + } + printf ("%02x", p[i]); + if (i % 2 == 1) + { + printf (" "); + } + if (i % 4 == 3) + { + printf (" "); + } + if (i % 16 == 15) + { + printf ("\n"); + } + } + printf ("\n"); +} + + +uint16_t +csum (uint16_t * buf, int count) +{ + uint32_t sum; + for (sum = 0; count > 0; count -= 2) + sum += *buf++; + sum = (sum >> 16) + (sum & 0xffff); + sum += (sum >> 16); + return (uint16_t) (~sum); +} + +uint16_t +csum_3 (uint16_t * buf1, int count1, uint16_t * buf2, int count2, + uint16_t * buf3, int count3) +{ + uint32_t sum; + for (sum = 0; count1 > 0; count1 -= 2) + sum += *buf1++; + for (; count2 > 0; count2 -= 2) + sum += *buf2++; + for (; count3 > 0; count3 -= 2) + sum += *buf3++; + sum = (sum >> 16) + (sum & 0xffff); + sum += (sum >> 16); + return (uint16_t) (~sum); +} + + + + +/* returns packet id */ +static u_int32_t +block_pkt (struct nfq_data *tb) +{ + int id = 0; + struct nfqnl_msg_packet_hdr *ph; + struct nfqnl_msg_packet_hw *hwph; + u_int32_t mark, ifi; + int ret; + int data_len; + int copy_len; + unsigned char *data; + uint16_t c; + + // Where to address the raw packets - fill this in + // as we go + struct sockaddr_ll socket_address; + memset (&socket_address, 0, sizeof (socket_address)); + + + // I'm tired of fighting all the crud, + // so I'm goingto just use a big block. + fullframe buffer; + memset (&buffer, 0, sizeof (buffer)); + assert (sizeof (buffer) == ETHER_TOTAL_SIZE); + + // We need + // Ethernet header + // IPv6 header + // IPCMPv6 header + // As much of the original payload as possible + // Final ether CRC + + + // Get the packet ID. NEeded for netfilter_queue response. + fprintf (stdout, "TRACE: %s %i\n", __FILE__, __LINE__); + ph = nfq_get_msg_packet_hdr (tb); + if (ph) + { + id = ntohl (ph->packet_id); + printf ("hw_protocol=0x%04x hook=%u id=%u ", + ntohs (ph->hw_protocol), ph->hook, id); + } + + // Get the data payload from netfilter_queue + fprintf (stdout, "TRACE: %s %i\n", __FILE__, __LINE__); + ret = nfq_get_payload (tb, &data); + if (ret >= 0) + printf ("payload_len=%d ", ret); + data_len = ret; + copy_len = (data_len > PAYLOAD_SIZE) ? PAYLOAD_SIZE : data_len; + printf ("copy_len=%d ", copy_len); + + + + // What MAC address sent us this packet? + // We intend to send the outbound packet + // back to the same place. + hwph = nfq_get_packet_hw (tb); + if (hwph) + { + int i, hlen = ntohs (hwph->hw_addrlen); + + printf ("hw_src_addr="); + for (i = 0; i < hlen - 1; i++) + printf ("%02x:", hwph->hw_addr[i]); + printf ("%02x ", hwph->hw_addr[hlen - 1]); + + // Ethernet frame destination + memcpy (&buffer.ether_frame[0], hwph->hw_addr, 6); + memcpy (socket_address.sll_addr, hwph->hw_addr, 6); + socket_address.sll_halen = ETH_ALEN; + } + + // TODO: Ethernet frame source + + // Ethernet frame type + buffer.ether_frame[12] = ETH_P_IPV6 / 256; + buffer.ether_frame[13] = ETH_P_IPV6 % 256; + + // Show the ethernet frame + hexdump ("DUMP: ether_frame", buffer.ether_frame, + sizeof (buffer.ether_frame)); + + + // Start creating the IPv6 header + buffer.ipv6_header[0] = 0x60; // IPv6 "version=6" + + // What is the payload length? + int plength = copy_len + ICMP6_SIZE; + buffer.ipv6_header[4] = plength / 256; + buffer.ipv6_header[5] = plength % 256; + + // What is the next header? + buffer.ipv6_header[6] = 0x3a; // ICMPv6 + buffer.ipv6_header[7] = 0xff; // Hop limit + + // Source address, Destination Address + // Just swap from what we saw in our input packet + memcpy (&buffer.ipv6_header[8], &data[24], 16); + memcpy (&buffer.ipv6_header[24], &data[8], 16); + + hexdump ("IP6 HEADER:", buffer.ipv6_header, sizeof (buffer.ipv6_header)); + + // ICMPv6 header + buffer.icmp6_header[0] = 2; // Type 2 Packet Too Big + buffer.icmp6_header[1] = 0; // Code (not used) + + // TODO Checksum + buffer.icmp6_header[2] = 0; // TODO Checksum + buffer.icmp6_header[3] = 0; // TODO checksum + + // MTU expressed as 32 bits + buffer.icmp6_header[4] = (MTU >> 24) & 0xff; + buffer.icmp6_header[5] = (MTU >> 16) & 0xff; + buffer.icmp6_header[6] = (MTU >> 8) & 0xff; + buffer.icmp6_header[7] = MTU & 0xff; + + memcpy (buffer.payload, data, copy_len); + hexdump ("ICMP6", buffer.icmp6_header, + sizeof (buffer.icmp6_header) + copy_len); + + u_int8_t pseudoheader[40]; + memcpy (pseudoheader, &buffer.ipv6_header[8], 32); + pseudoheader[32] = 0; // length never more than 0xffff + pseudoheader[33] = 0; // length never more than 0xffff + pseudoheader[34] = (ICMP6_SIZE + copy_len) / 256; + pseudoheader[35] = (ICMP6_SIZE + copy_len) % 256; + pseudoheader[36] = 0; // zero + pseudoheader[37] = 0; // zero + pseudoheader[38] = 0; // zero + pseudoheader[39] = 58; // ICMPv6 header code + + c = csum_3 ((uint16_t *) pseudoheader, sizeof (pseudoheader), + (uint16_t *) buffer.icmp6_header, sizeof (buffer.icmp6_header), + (uint16_t *) buffer.payload, copy_len); + buffer.icmp6_header[2] = c % 256; + buffer.icmp6_header[3] = c / 256; + + hexdump ("PseudoHeader", pseudoheader, sizeof (pseudoheader)); + + + hexdump ("ICMP6", buffer.icmp6_header, + sizeof (buffer.icmp6_header) + copy_len); + + + + + + + // Device ID that the packet came from + fprintf (stdout, "TRACE: %s %i\n", __FILE__, __LINE__); + ifi = nfq_get_indev (tb); + if (ifi) + { + printf ("indev=%u ", ifi); + socket_address.sll_ifindex = ifi; + memcpy (&buffer.ether_frame[6], macaddr_for_interface (ifi), 6); + } + + fputc ('\n', stdout); + fprintf (stdout, "TRACE: %s %i\n", __FILE__, __LINE__); + + int tx_len = ETHER_SIZE + IPV6HDR_SIZE + ICMP6_SIZE + copy_len; + if (sendto + (sockfd (), &buffer, tx_len, 0, (struct sockaddr *) &socket_address, + sizeof (struct sockaddr_ll)) < 0) + printf ("Send failed\n"); + + + + + return id; +} + + + +/* returns packet id */ +static u_int32_t +print_pkt (struct nfq_data *tb) +{ + int id = 0; + struct nfqnl_msg_packet_hdr *ph; + struct nfqnl_msg_packet_hw *hwph; + u_int32_t mark, ifi; + int ret; + unsigned char *data; + + ph = nfq_get_msg_packet_hdr (tb); + if (ph) + { + id = ntohl (ph->packet_id); + printf ("hw_protocol=0x%04x hook=%u id=%u ", + ntohs (ph->hw_protocol), ph->hook, id); + } + + hwph = nfq_get_packet_hw (tb); + if (hwph) + { + int i, hlen = ntohs (hwph->hw_addrlen); + + printf ("hw_src_addr="); + for (i = 0; i < hlen - 1; i++) + printf ("%02x:", hwph->hw_addr[i]); + printf ("%02x ", hwph->hw_addr[hlen - 1]); + } + + mark = nfq_get_nfmark (tb); + if (mark) + printf ("mark=%u ", mark); + + ifi = nfq_get_indev (tb); + if (ifi) + printf ("indev=%u ", ifi); + + ifi = nfq_get_outdev (tb); + if (ifi) + printf ("outdev=%u ", ifi); + ifi = nfq_get_physindev (tb); + if (ifi) + printf ("physindev=%u ", ifi); + + ifi = nfq_get_physoutdev (tb); + if (ifi) + printf ("physoutdev=%u ", ifi); + + ret = nfq_get_payload (tb, &data); + if (ret >= 0) + printf ("payload_len=%d ", ret); + + fputc ('\n', stdout); + + return id; +} + + +static int +cb (struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, + struct nfq_data *nfa, void *data) +{ + static u_int32_t id; +// id = print_pkt(nfa); + printf ("entering callback\n"); + id = block_pkt (nfa); + return nfq_set_verdict (qh, id, NF_DROP, 0, NULL); +} + + +int +main (int argc, char **argv) +{ + struct nfq_handle *h; + struct nfq_q_handle *qh; + struct nfnl_handle *nh; + int fd; + int rv; + unsigned int queue = 1280; // -q + unsigned int do_fork = 0; // -d + char *interface; + char buf[4096] __attribute__ ((aligned)); + +// Getopt + int c; + int opterr = 0; + while ((c = getopt (argc, argv, "dq:")) != -1) + switch (c) + { + case 'd': + fprintf (stderr, "setting\n"); + do_fork = 1; + fprintf (stderr, "set!\n"); + break; + case 'q': + queue = strtol (optarg, NULL, 10); + break; + case '?': + if (optopt == 'q') + fprintf (stderr, "Option -%c requires an argument.\n", optopt); + else if (isprint (optopt)) + fprintf (stderr, "Unknown option `-%c'.\n", optopt); + else + fprintf (stderr, "Unknown option character `\\x%x'.\n", optopt); + return 1; + default: + abort (); + } + + + + + printf ("opening library handle\n"); + h = nfq_open (); + if (!h) + { + fprintf (stdout, "error during nfq_open()\n"); + exit (1); + } + + printf ("unbinding existing nf_queue handler for AF_INET6 (if any)\n"); + if (nfq_unbind_pf (h, AF_INET6) < 0) + { + fprintf (stdout, "error during nfq_unbind_pf()\n"); + if (getuid () != 0) + { + fprintf (stderr, "%s: must be ran as root\n", argv[0]); + } + exit (1); + } + + printf ("binding nfnetlink_queue as nf_queue handler for AF_INET6\n"); + if (nfq_bind_pf (h, AF_INET6) < 0) + { + fprintf (stdout, "error during nfq_bind_pf()\n"); + exit (1); + } + + printf ("binding this socket to queue '%u'\n", queue); + qh = nfq_create_queue (h, queue, &cb, NULL); + if (!qh) + { + fprintf (stdout, "error during nfq_create_queue()\n"); + exit (1); + } + + + printf ("setting copy_packet mode\n"); + if (nfq_set_mode (qh, NFQNL_COPY_PACKET, 0xffff) < 0) + { + fprintf (stdout, "can't set packet_copy mode\n"); + exit (1); + } + + + if (do_fork) + { + fprintf (stdout, "forking to background\n"); + daemon (0, 0); + } + + + fd = nfq_fd (h); + + while ((rv = recv (fd, buf, sizeof (buf), 0)) && rv >= 0) + { + printf ("pkt received\n"); + nfq_handle_packet (h, buf, rv); + } + + printf ("unbinding from queue 0\n"); + nfq_destroy_queue (qh); + +#ifdef INSANE + /* normally, applications SHOULD NOT issue this command, since + * it detaches other programs/sockets from AF_INET6, too ! */ + printf ("unbinding from AF_INET6\n"); + nfq_unbind_pf (h, AF_INET6); +#endif + + printf ("closing library handle\n"); + nfq_close (h); + + exit (0); +} |