Hi NetDev and Google,

The answer to my question was:
 Aha, you are trying to use tcpdump to see the "encapsulated" packets,
then you also need to adjust skb->mac.raw.

I have attached the code (and inlined the receive func, for easier
commenting), as I have another question:

I use dev_get_by_index() which calls dev_hold() (via my function
m88e_get_netdevice()).

The question is: 
  Should I use dev_put() before or after calling netif_rx(skb).

-- 
Med venlig hilsen / Best regards
  Jesper Brouer
  ComX Networks A/S
  Linux Network developer
  Cand. Scient Datalog / MSc.
  Author of http://adsl-optimizer.dk
  LinkedIn: http://www.linkedin.com/in/brouer


On Fri, 2008-02-01 at 14:28 +0100, Jesper Dangaard Brouer wrote:
> Hi Netdev
> 
> I writing a new protocol handler using dev_add_pack().  (For a Marvell
> switch chip handling DSA (Distributed Switch Architecture) Ethertype
> packets).
> 
> My protocol handler works and I get the skb. But I want to remove the
> DSA Headers and send the packet back for normal processing on a
> device. (I actually just want to be able to tcpdump these packets on
> the device).
> 
> I'm removing the headers by:
>   skb_pull(skb, sizeof(struct dsa_header));
> 
> I'm trying to retransmit it by:
>   netif_rx(skb);
> 
> But it seems that I just retransmit the same packet without removing
> the DSA headers.
> 
> Any hints about which functions I should use the remove the DSA header?



/*
 *      Main DSA Receive routine.
 */
int dsa_receive(struct sk_buff *skb, struct net_device *dev, struct packet_type 
*pt)
{
        int res = NET_RX_SUCCESS;
        struct dsa_header   *dsa_hdr;
        struct proto_header *proto_hdr;
        struct net_device   *port_dev; /* the port associated net_device*/
        struct ethhdr       *eth_hdr;

        /*
        if      (skb->pkt_type == PACKET_OTHERHOST) VERBOSE("PACKET_OTHERHOST");
        else if (skb->pkt_type == PACKET_BROADCAST) VERBOSE("PACKET_BROADCAST");
        else if (skb->pkt_type == PACKET_MULTICAST) VERBOSE("PACKET_MULTICAST");
        else if (skb->pkt_type == PACKET_HOST)      VERBOSE("PACKET_HOST");
        else VERBOSE("PACKET_TYPE UNKNOWN");
        */

        // Access to the real mac header
        eth_hdr = (struct ethhdr *)skb->mac.raw; // Same as eth_hdr(skb)

        // At this point the Ethernet header has been removed, but the
        // DSA EtherType header contains two extra reserved bytes,
        // move the pointer past these.
        //
        // DEBUGOUT("Removing 2 reserved bytes from DSA EtherType tag");
        skb_pull(skb, 2);

        // Access to the DSA packet information
        dsa_hdr = (struct dsa_header *) skb->data;

        if (net_ratelimit()) {
                printk(KERN_INFO);
                printk("Dev:%s ",      skb->dev->name);
                printk("Type 0x%x ",   dsa_hdr->type);
                printk("Tagged 0x%x ", dsa_hdr->tagged);
//              printk("Dev 0x%x ",    dsa_hdr->dev);
                printk("Port 0x%x ",   dsa_hdr->port);
                printk("Info 0x%x ",   dsa_hdr->info);
                printk("Prio 0x%x ",   dsa_hdr->pri);
                printk("Res 0x%x ",    dsa_hdr->res);
                printk("VLAN ID 0x%x ",dsa_hdr->vid);
                printk("\n");
        }

        // Decoding the DSA type
        switch(dsa_hdr->type) {
        case DSA_TO_CPU:
        {
                int code = ((dsa_hdr->info)&0x06)|dsa_hdr->res;

                if(IGMP_TRAP == code) {
                        printk("IGMP Trap\n");
                } else {
                        printk("Unsupported code %d\n",code);
                }
                break;
        }
        case DSA_FROM_CPU:
                /*Fall through*/
        case DSA_TO_SNIFFER:
                /*Fall through*/
                if (net_ratelimit())
                        DEBUGOUT("Sniffer packet");
                break;
        case DSA_FORWARD:
                /*Fall through*/
        default:

                break;
        }

        // Find the device associated with the switch port.
        //
        // Q: m88e_get_netdevice() calls dev_get_by_index(), which it calls 
dev_hold()
        port_dev = m88e_get_netdevice(dsa_hdr->port);
        skb->dev = port_dev;

        // Move past the DSA header
        skb_pull(skb, sizeof(struct dsa_header));

        // TEST: if it possible to change some skb data and see it in tcpdump
        // eth_hdr->h_proto = 0x9111;

        /* Extract the ethertype of the encapsulated packet, and
           indicate the protocol to the next layer. */
        proto_hdr     = (struct proto_header*) skb->data;
        skb->protocol = proto_hdr->h_proto;

        if (net_ratelimit())
                printk(KERN_ERR "Encapsulated packet Protocol 
0x%x\n",skb->protocol);

        // HACK, shift the mac header, this allows packet sniffers to
        // decode the packet as a normal ethernet packet, but the mac
        // address will not be correct.
        skb->mac.raw = skb->data - (6+6);

        // Q: Is it allowed to modify the skb data, I would like to
        // correct the MAC headers, but is that allowed? Do I need to
        // skb_copy the packet?

        // Move past the EtherType of the encapsulated packet
        skb_pull(skb, sizeof(struct proto_header));

        // FIXME: Perhaps we need to change skb->pkt_type to
        // PACKET_HOST, to make the next protocol handlers
        // accept/process the data???
        //skb->pkt_type = PACKET_HOST;

        // Retransmit it on a virtual switch port device... this will
        // e.g. send it to the IGMP protocol handler.
        res = netif_rx(skb);

        // Release the device pointer
        if (port_dev)
                dev_put(port_dev);

        return res;

 drop:
        kfree_skb(skb);
 out:
        return NET_RX_DROP;
}


static struct packet_type dsa_packet_type =
{
        .type = __constant_htons(ETHERTYPE_DSA),
        .dev  = NULL, /* NULL is wildcarded interfaces */
        .func = dsa_receive,
        .data = NULL,
        //.data = (void*)1, /* Set here '(void *)1' when this code can SHARE 
SKBs */
        .next = NULL
};

/*
 * Marvell DSA (Distributed Switch Architecture) Ethertype handling.
 *
 *  Some Marvell switch chips support to send/trap/mirror special
 *  packets to another switch port.  These special packets can contain
 *  some extra information stored in (whats called) a DSA tag.  The
 *  chip also supports adding a special EtherType in front of the DSA
 *  tag, allowing a CPU (which attached to one of the switch ports)
 *  easier identification of DSA frames.
 *
 * Copyright (C) Jesper Dangaard Brouer, Visipia Aps.
 *
 *	This program is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation; either version 2 of the License, or
 *	(at your option) any later version.
 *
 *	This program is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *	GNU General Public License for more details.
 *
 *	You should have received a copy of the GNU General Public License
 *	along with this program; if not, write to the Free Software
 *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * SVN version info:
 * 	$LastChangedDate: 2008-02-11 11:11:28 +0100 (Mon, 11 Feb 2008) $
 * 	$LastChangedRevision: 5238 $
 * 	$LastChangedBy: jdb $
 *
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include "ethertype_dsa.h"

#define MODULE_NAME "m88e_DSA"
#define M88E_DEBUG   1
#define M88E_VERBOSE 1
#include "m88e.h"

#ifndef NOTICE /* Handle if "m88e.h" debug macros are not included */
#define NOTICE(S)          printk(KERN_NOTICE "NOTICE: " S "\n")
#define DEBUGOUT(S)        printk(KERN_DEBUG S "\n")
#define DEBUGOUT1(S, A...) printk(KERN_DEBUG S "\n", A)
#endif/*NOTICE*/

#include <linux/netdevice.h> /* struct packet_type */
#include <linux/if_ether.h>  /* struct ethhdr */

#include "m88e_dev.h" /* Access to port associated net_device's */

/*
 Default EtherType set by Marvell is 0x9100, this should be changed
  1) because its conflicts with double-tagging of VLANs
  2) because it hits the same EtherType hash bucket (ptype_base) as IP and VLAN
*/
#define ETHERTYPE_DSA 0x9100

struct proto_header
{
	unsigned short	h_proto;		/* packet type ID field	*/
} __attribute__((packed));


/*
 * 	Main DSA Receive routine.
 */
int dsa_receive(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt)
{
	int res = NET_RX_SUCCESS;
	struct dsa_header   *dsa_hdr;
	struct proto_header *proto_hdr;
	struct net_device   *port_dev; /* the port associated net_device*/
	struct ethhdr       *eth_hdr;

	/*
	if      (skb->pkt_type == PACKET_OTHERHOST) VERBOSE("PACKET_OTHERHOST");
	else if (skb->pkt_type == PACKET_BROADCAST) VERBOSE("PACKET_BROADCAST");
	else if (skb->pkt_type == PACKET_MULTICAST) VERBOSE("PACKET_MULTICAST");
	else if (skb->pkt_type == PACKET_HOST)      VERBOSE("PACKET_HOST");
	else VERBOSE("PACKET_TYPE UNKNOWN");
	*/

	// Access to the real mac header
	eth_hdr = (struct ethhdr *)skb->mac.raw; // Same as eth_hdr(skb)

	// At this point the Ethernet header has been removed, but the
	// DSA EtherType header contains two extra reserved bytes,
	// move the pointer past these.
	//
	// DEBUGOUT("Removing 2 reserved bytes from DSA EtherType tag");
	skb_pull(skb, 2);

	// Access to the DSA packet information
	dsa_hdr = (struct dsa_header *) skb->data;

	if (net_ratelimit()) {
		printk(KERN_INFO);
		printk("Dev:%s ",      skb->dev->name);
		printk("Type 0x%x ",   dsa_hdr->type);
		printk("Tagged 0x%x ", dsa_hdr->tagged);
//		printk("Dev 0x%x ",    dsa_hdr->dev);
		printk("Port 0x%x ",   dsa_hdr->port);
		printk("Info 0x%x ",   dsa_hdr->info);
		printk("Prio 0x%x ",   dsa_hdr->pri);
		printk("Res 0x%x ",    dsa_hdr->res);
		printk("VLAN ID 0x%x ",dsa_hdr->vid);
		printk("\n");
	}

	// Decoding the DSA type
	switch(dsa_hdr->type) {
	case DSA_TO_CPU:
	{
		int code = ((dsa_hdr->info)&0x06)|dsa_hdr->res;

		if(IGMP_TRAP == code) {
			printk("IGMP Trap\n");
		} else {
			printk("Unsupported code %d\n",code);
		}
		break;
	}
	case DSA_FROM_CPU:
		/*Fall through*/
	case DSA_TO_SNIFFER:
		/*Fall through*/
		if (net_ratelimit())
			DEBUGOUT("Sniffer packet");
		break;
	case DSA_FORWARD:
		/*Fall through*/
	default:

		break;
	}

	// Find the device associated with the switch port.
	port_dev = m88e_get_netdevice(dsa_hdr->port);
	skb->dev = port_dev;

	// Move past the DSA header
	skb_pull(skb, sizeof(struct dsa_header));

	// TEST: if it possible to change some skb data and see it in tcpdump
	// eth_hdr->h_proto = 0x9111;

	/* Extract the ethertype of the encapsulated packet, and
	   indicate the protocol to the next layer. */
	proto_hdr     = (struct proto_header*) skb->data;
	skb->protocol = proto_hdr->h_proto;

	if (net_ratelimit())
		printk(KERN_ERR "Encapsulated packet Protocol 0x%x\n",skb->protocol);

	// HACK, shift the mac header, this allows packet sniffers to
	// decode the packet as a normal ethernet packet, but the mac
	// address will not be correct.
	skb->mac.raw = skb->data - (6+6);

	// Q: Is it allowed to modify the skb data, I would like to
	// correct the MAC headers, but is that allowed? Do I need to
	// skb_copy the packet?

	// Move past the EtherType of the encapsulated packet
	skb_pull(skb, sizeof(struct proto_header));

	// FIXME: Perhaps we need to change skb->pkt_type to
	// PACKET_HOST, to make the next protocol handlers
	// accept/process the data???
	//skb->pkt_type = PACKET_HOST;

	// Retransmit it on a virtual switch port device... this will
	// e.g. send it to the IGMP protocol handler.
	res = netif_rx(skb);

	// Release the device pointer
	if (port_dev)
		dev_put(port_dev);

	return res;

 drop:
        kfree_skb(skb);
 out:
        return NET_RX_DROP;
}


static struct packet_type dsa_packet_type =
{
	.type = __constant_htons(ETHERTYPE_DSA),
	.dev  = NULL, /* NULL is wildcarded interfaces */
	.func = dsa_receive,
	.data = NULL,
	//.data = (void*)1, /* Set here '(void *)1' when this code can SHARE SKBs */
	.next = NULL
};

void dsa_register_handler(void)
{
	// Only process DSA packets on CPU to switch device, trying to
	// avoid loops.
	dsa_packet_type.dev = dev_get_by_name("ixp1");

	// TODO: Increase the MTU on the receiving device, the DSA
	// EtherType is 4 bytes and the DSA info is 4 bytes.

	NOTICE("Register: DSA EtherType packet handler");
	dev_add_pack(&dsa_packet_type);
}

void dsa_unregister_handler(void)
{
	NOTICE("Unregister: DSA EtherType packet handler");
	dev_remove_pack(&dsa_packet_type);
}
/*
 * Marvell DSA (Distributed Switch Architecture) Ethertype handling.
 *
 * Copyright (C) Jesper Dangaard Brouer, Visipia Aps.
 *
 *	This program is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation; either version 2 of the License, or
 *	(at your option) any later version.
 *
 *	This program is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *	GNU General Public License for more details.
 *
 *	You should have received a copy of the GNU General Public License
 *	along with this program; if not, write to the Free Software
 *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * SVN version info:
 * 	$LastChangedDate: 2008-02-11 11:11:28 +0100 (Mon, 11 Feb 2008) $
 * 	$LastChangedRevision: 5238 $
 * 	$LastChangedBy: jdb $
 *
 */


#ifndef _ethertype_dsa_H_
#define _ethertype_dsa_H_

#include <linux/types.h>
#include <asm/byteorder.h>

struct dsa_header {
	__u8  type:2,
	      tagged:1,
	      dev:5;
	__u8  port:5,
	      info:3; // Decoding dependent on type
#if defined(__BIG_ENDIAN_BITFIELD)
	__u16 pri:3,
	      res:1,
	      vid:12;
#elif defined(__LITTLE_ENDIAN_BITFIELD)
#error	"Not defined for Little Endian CPUs"
#else
#error	"Adjust your <asm/byteorder.h> defines"
#endif
};

// DSA Tag Types
enum {
	DSA_TO_CPU     = 0x0, // b00
	DSA_FROM_CPU   = 0x1, // b01
	DSA_TO_SNIFFER = 0x2, // b10
	DSA_FORWARD    = 0x3, // b11
};

// DSA_TO_CPU Support codes
enum {
        BDPU_TRAP          = 0x0, // b000
	FRAME2REG_RESPONSE = 0x1, // b001
	IGMP_TRAP          = 0x2, // b010
	POLICY_TRAP        = 0x3, // b011
	ARP_MIRROR         = 0x4, // b100
	POLICY_MIRROR      = 0x5, // b101
};


extern void dsa_register_handler  (void);
extern void dsa_unregister_handler(void);

#endif

Reply via email to