summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Fesler <jfesler@guest.gigo.com>2015-02-17 10:55:38 -0800
committerJason Fesler <jfesler@guest.gigo.com>2015-02-17 10:55:38 -0800
commite6123d6e145f4e35d8f3e004891a5645d2e11197 (patch)
tree570d5766345dd004bddbc162d8ed36501c97eb51
parent5403487c00490d624451079c66aaad4f05f493a3 (diff)
downloadmtu1280d-e6123d6e145f4e35d8f3e004891a5645d2e11197.tar.gz
mtu1280d-e6123d6e145f4e35d8f3e004891a5645d2e11197.zip
wip
-rw-r--r--README34
-rw-r--r--mtu1280.c446
-rw-r--r--originals/libnetfilter_queue-1.0.2.tar.bz2bin0 -> 354201 bytes
3 files changed, 480 insertions, 0 deletions
diff --git a/README b/README
new file mode 100644
index 0000000..647f751
--- /dev/null
+++ b/README
@@ -0,0 +1,34 @@
+mtu1280 - emulates serving via a low MTU IPv6 tunnel
+----------------------------------------------------
+
+This program is will generate ICMPv6 "Packet Too Big"
+responses with an MTU of 1280. mtu1280 will connect to
+a netfilter_queue socket, listening for packets; and
+respond to all packets sent to that queue.
+
+This is meant to be ran on a secondary IP for your host.
+It is recommend that your primary IP is NOT used with
+this technique in case of application failure.
+
+To deploy, compile build and install. Copy
+in one of the init or init.d scripts, and make sure
+it is set for auto-start for your OS. An actual reboot
+is recommended.
+
+Once up and running, configure ip6tables to route
+large packets destined to the desired IP to the netfilter queue.
+
+Example rule:
+
+```
+guest% sudo ip6tables-save | grep NFQ
+-A INPUT -d 2001:470:1f04:d63::2/128 -m length --length 1281:65535 -j -NFQUEUE --queue-num 1280
+```
+
+
+LICENSE
+-------
+GPLv2, due to the duplicated code from Hararld Welte's
+libnetfilter_queue-1.0.2/utils/nfqnl_test.c (included).
+
+
diff --git a/mtu1280.c b/mtu1280.c
new file mode 100644
index 0000000..6863c64
--- /dev/null
+++ b/mtu1280.c
@@ -0,0 +1,446 @@
+// Thanks to Austin Marton
+// https://austinmarton.wordpress.com/2011/09/14/sending-raw-ethernet-packets-from-a-specific-interface-in-c-on-linux/
+
+// Portions of this file derived from libnetfilter_queue-1.0.2/utils/nfqnl_test.c
+// Copyright by Harald Welte
+
+#include <stdlib.h>
+#include <stdio.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;
+ char *interface;
+ char buf[4096] __attribute__ ((aligned));
+
+ if (argc != 2) {
+ fprintf(stdout,"usage: a.out netgroup_number\n");
+ exit(1);
+ }
+ queue = strtol(argv[1],NULL,10);
+
+
+ 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");
+ 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);
+ }
+
+ 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);
+}
+
diff --git a/originals/libnetfilter_queue-1.0.2.tar.bz2 b/originals/libnetfilter_queue-1.0.2.tar.bz2
new file mode 100644
index 0000000..2f6f46c
--- /dev/null
+++ b/originals/libnetfilter_queue-1.0.2.tar.bz2
Binary files differ