Spoofing Multicast packet from another interface.

This is the scenario. I have a working ethernet device eth0 with an IP address 192.168.1.x. There is one more special interface in the system, say if1 which is connected to another network. That network is mainly used for transmitting multicast data.

Received multicast data has to be processed by a userland application. Due to some reason I want to project this multicast data as unicast, with a destination MAC as the MAC address of eth0 and destination IP address as the IP address of eth0. So, the application feels that the multicast is coming through eth0 interface. This has to be done for UDP packets to port 10000 only.

This is achieved by changing the driver of if1 interface the following way.

#include <ltnet/checksum.h>
#include <ltlinux/inetdevice.h>


static struct net_device *eth_dev;
static u32 local_ip;
static int init_done = 0;


static void init_fwd_process(struct net_device* net_dev)
{
  /* Here net_dev is the if1 device */
  struct in_ifaddr* ifa;

  if(likely(init_done == 1))
    return;

  /* Find out the net_device of eth0 */
  eth_dev = dev_get_by_name(&init_net, "eth0");

  /* First find out the IP address of eth0 */
  ifa = ((struct in_device*)eth_dev->ip_ptr)->ifa_list;
  local_ip = ifa->ifa_address;

  init_done = 1;
}


/* Initial 2 octets are used by the driver, for this particular driver */
#define ETH_DST_OFF 2
#define ETH_SRC_OFF 16
#define ETH_TYPE_OFF 30
#define IP_HDR_OFF 32
#define IP_TYPE_OFF (IP_HDR_OFF + 9)
#define IP_DSTABSOLUTE_OFF (IP_HDR_OFF + 16)
#define UDP_HDR_OFF (IP_HDR_OFF + 20)
#define UDP_DPORT_OFF (UDP_HDR_OFF + 2)

#define IP_TYPE_UDP 0x11

/* These are relative to IP packet */
#define IP_SRC_OFF 12
#define IP_DST_OFF 16
#define IP_CS_OFF 10
#define UDP_CS_OFF (20 + 6)

/* UDP Port we are interested in, 10000 in this case */
#define UDP_PORT 10000

static int fwd_mcast_packet(struct sk_buff *skb, unsigned char *data,
  int len, struct net_device  *net_dev)
{
  int n;
  u16 c;
  unsigned char* eth_src;
  unsigned char* eth_type;
  unsigned char* ip_hdr;
  unsigned char* ip_type;
  u32 ip_dst;
  u16 udp_dst;
  unsigned char hdr_802_3[14];

  do {
    init_fwd_process(net_dev);

    eth_src = data + ETH_SRC_OFF;
    eth_type = data + ETH_TYPE_OFF;
    ip_hdr = data + IP_HDR_OFF;
    ip_type = data + IP_TYPE_OFF;
    ip_dst = (u32)(ntohl(*((u32 *)(data + IP_DSTABSOLUTE_OFF))));
    udp_dst = (u16)(ntohs(*((u16 *)(data + UDP_DPORT_OFF))));

    /* only IP packet */
    if((*(u16 *)eth_type) != (htons(ETH_P_IP)))
      break;
    /* only UDP */
    if(*ip_type != IP_TYPE_UDP)
      break;
    /* only multicast */
    if(!(ntohl(ip_dst) & 0xe0000000))
      break;
    /* only interesting port */
    if(ntohs(udp_dst) != UDP_PORT)
      break;

    /* We are interested in this packet
    Let other packets go through the usual path */
    /* destination is eth0 */
    memcpy(&hdr_802_3[0], eth_dev->dev_addr, 6);
    /* source is the original one */
    memcpy(&hdr_802_3[6], eth_src, 6);
    /* ethernet type is the original one */
    memcpy(&hdr_802_3[12], eth_type, 2);

    /* for aligning IP header on 16 byte */
    skb_reserve(skb, 2);
    /* copy the generated MAC header */
    memcpy(skb_put(skb, 14), hdr_802_3, 14);

    /* copy from the original IP header onwards */
    n = len - IP_HDR_OFF;
    memcpy(skb_put(skb, n), ip_hdr, n);

    skb->protocol = htons(ETH_P_IP);
    skb->pkt_type = PACKET_HOST;
    skb_reset_mac_header(skb); /* just like eth_type_trans */
    skb_pull(skb, ETH_HLEN);

    /* Destination IP address should be local_ip,
    Here skb->data is the beginning of IP header */
    memcpy(&skb->data[IP_DST_OFF], &local_ip, 4);

    /* regenerate IP checksum */
    skb->data[IP_CS_OFF] = 0;
    skb->data[IP_CS_OFF + 1] = 0;
    c = ip_fast_csum(skb->data, 5);
    memcpy(&skb->data[IP_CS_OFF], &c, 2);

    /* Only clear the UDP checksum */
    skb->data[UDP_CS_OFF] = 0;
    skb->data[UDP_CS_OFF + 1] = 0;

    /* call the main receive function */
    netif_rx(skb);

    return len;
  }while(0);

  return -1;
}

/* Inside the main packet receive function of driver */
  if(fwd_mcast_packet(skb, (unsigned char *)data, length, net_dev) > 0) {
    /* This packet has been already forwarded to IP stack,
    Just do no do anything */
    return;
  }

Advertisement

Published by Anand Sivaram (आनन्दः )

twitter.com/anand_sivaram https://www.linkedin.com/in/anandsivaram/

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: