/* * 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 #include #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 /* struct packet_type */ #include /* 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); }