mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-04 23:47:46 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
10
net/802/Kconfig
Normal file
10
net/802/Kconfig
Normal file
|
@ -0,0 +1,10 @@
|
|||
config STP
|
||||
tristate
|
||||
select LLC
|
||||
|
||||
config GARP
|
||||
tristate
|
||||
select STP
|
||||
|
||||
config MRP
|
||||
tristate
|
14
net/802/Makefile
Normal file
14
net/802/Makefile
Normal file
|
@ -0,0 +1,14 @@
|
|||
#
|
||||
# Makefile for the Linux 802.x protocol layers.
|
||||
#
|
||||
|
||||
# Check the p8022 selections against net/core/Makefile.
|
||||
obj-$(CONFIG_LLC) += p8022.o psnap.o
|
||||
obj-$(CONFIG_NET_FC) += fc.o
|
||||
obj-$(CONFIG_FDDI) += fddi.o
|
||||
obj-$(CONFIG_HIPPI) += hippi.o
|
||||
obj-$(CONFIG_IPX) += p8022.o psnap.o p8023.o
|
||||
obj-$(CONFIG_ATALK) += p8022.o psnap.o
|
||||
obj-$(CONFIG_STP) += stp.o
|
||||
obj-$(CONFIG_GARP) += garp.o
|
||||
obj-$(CONFIG_MRP) += mrp.o
|
131
net/802/fc.c
Normal file
131
net/802/fc.c
Normal file
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* NET3: Fibre Channel device handling subroutines
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Vineet Abraham <vma@iol.unh.edu>
|
||||
* v 1.0 03/22/99
|
||||
*/
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/inet.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/fcdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/export.h>
|
||||
#include <net/arp.h>
|
||||
|
||||
/*
|
||||
* Put the headers on a Fibre Channel packet.
|
||||
*/
|
||||
|
||||
static int fc_header(struct sk_buff *skb, struct net_device *dev,
|
||||
unsigned short type,
|
||||
const void *daddr, const void *saddr, unsigned int len)
|
||||
{
|
||||
struct fch_hdr *fch;
|
||||
int hdr_len;
|
||||
|
||||
/*
|
||||
* Add the 802.2 SNAP header if IP as the IPv4 code calls
|
||||
* dev->hard_header directly.
|
||||
*/
|
||||
if (type == ETH_P_IP || type == ETH_P_ARP)
|
||||
{
|
||||
struct fcllc *fcllc;
|
||||
|
||||
hdr_len = sizeof(struct fch_hdr) + sizeof(struct fcllc);
|
||||
fch = (struct fch_hdr *)skb_push(skb, hdr_len);
|
||||
fcllc = (struct fcllc *)(fch+1);
|
||||
fcllc->dsap = fcllc->ssap = EXTENDED_SAP;
|
||||
fcllc->llc = UI_CMD;
|
||||
fcllc->protid[0] = fcllc->protid[1] = fcllc->protid[2] = 0x00;
|
||||
fcllc->ethertype = htons(type);
|
||||
}
|
||||
else
|
||||
{
|
||||
hdr_len = sizeof(struct fch_hdr);
|
||||
fch = (struct fch_hdr *)skb_push(skb, hdr_len);
|
||||
}
|
||||
|
||||
if(saddr)
|
||||
memcpy(fch->saddr,saddr,dev->addr_len);
|
||||
else
|
||||
memcpy(fch->saddr,dev->dev_addr,dev->addr_len);
|
||||
|
||||
if(daddr)
|
||||
{
|
||||
memcpy(fch->daddr,daddr,dev->addr_len);
|
||||
return hdr_len;
|
||||
}
|
||||
return -hdr_len;
|
||||
}
|
||||
|
||||
/*
|
||||
* A neighbour discovery of some species (eg arp) has completed. We
|
||||
* can now send the packet.
|
||||
*/
|
||||
|
||||
static int fc_rebuild_header(struct sk_buff *skb)
|
||||
{
|
||||
#ifdef CONFIG_INET
|
||||
struct fch_hdr *fch=(struct fch_hdr *)skb->data;
|
||||
struct fcllc *fcllc=(struct fcllc *)(skb->data+sizeof(struct fch_hdr));
|
||||
if(fcllc->ethertype != htons(ETH_P_IP)) {
|
||||
printk("fc_rebuild_header: Don't know how to resolve type %04X addresses ?\n", ntohs(fcllc->ethertype));
|
||||
return 0;
|
||||
}
|
||||
return arp_find(fch->daddr, skb);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static const struct header_ops fc_header_ops = {
|
||||
.create = fc_header,
|
||||
.rebuild = fc_rebuild_header,
|
||||
};
|
||||
|
||||
static void fc_setup(struct net_device *dev)
|
||||
{
|
||||
dev->header_ops = &fc_header_ops;
|
||||
dev->type = ARPHRD_IEEE802;
|
||||
dev->hard_header_len = FC_HLEN;
|
||||
dev->mtu = 2024;
|
||||
dev->addr_len = FC_ALEN;
|
||||
dev->tx_queue_len = 100; /* Long queues on fc */
|
||||
dev->flags = IFF_BROADCAST;
|
||||
|
||||
memset(dev->broadcast, 0xFF, FC_ALEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* alloc_fcdev - Register fibre channel device
|
||||
* @sizeof_priv: Size of additional driver-private structure to be allocated
|
||||
* for this fibre channel device
|
||||
*
|
||||
* Fill in the fields of the device structure with fibre channel-generic values.
|
||||
*
|
||||
* Constructs a new net device, complete with a private data area of
|
||||
* size @sizeof_priv. A 32-byte (not bit) alignment is enforced for
|
||||
* this private data area.
|
||||
*/
|
||||
struct net_device *alloc_fcdev(int sizeof_priv)
|
||||
{
|
||||
return alloc_netdev(sizeof_priv, "fc%d", NET_NAME_UNKNOWN, fc_setup);
|
||||
}
|
||||
EXPORT_SYMBOL(alloc_fcdev);
|
215
net/802/fddi.c
Normal file
215
net/802/fddi.c
Normal file
|
@ -0,0 +1,215 @@
|
|||
/*
|
||||
* INET An implementation of the TCP/IP protocol suite for the LINUX
|
||||
* operating system. INET is implemented using the BSD Socket
|
||||
* interface as the means of communication with the user level.
|
||||
*
|
||||
* FDDI-type device handling.
|
||||
*
|
||||
* Version: @(#)fddi.c 1.0.0 08/12/96
|
||||
*
|
||||
* Authors: Lawrence V. Stefani, <stefani@lkg.dec.com>
|
||||
*
|
||||
* fddi.c is based on previous eth.c and tr.c work by
|
||||
* Ross Biro
|
||||
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
|
||||
* Mark Evans, <evansmp@uhura.aston.ac.uk>
|
||||
* Florian La Roche, <rzsfl@rz.uni-sb.de>
|
||||
* Alan Cox, <gw4pts@gw4pts.ampr.org>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Changes
|
||||
* Alan Cox : New arp/rebuild header
|
||||
* Maciej W. Rozycki : IPv6 support
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/inet.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/fddidevice.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/errno.h>
|
||||
#include <net/arp.h>
|
||||
#include <net/sock.h>
|
||||
|
||||
/*
|
||||
* Create the FDDI MAC header for an arbitrary protocol layer
|
||||
*
|
||||
* saddr=NULL means use device source address
|
||||
* daddr=NULL means leave destination address (eg unresolved arp)
|
||||
*/
|
||||
|
||||
static int fddi_header(struct sk_buff *skb, struct net_device *dev,
|
||||
unsigned short type,
|
||||
const void *daddr, const void *saddr, unsigned int len)
|
||||
{
|
||||
int hl = FDDI_K_SNAP_HLEN;
|
||||
struct fddihdr *fddi;
|
||||
|
||||
if(type != ETH_P_IP && type != ETH_P_IPV6 && type != ETH_P_ARP)
|
||||
hl=FDDI_K_8022_HLEN-3;
|
||||
fddi = (struct fddihdr *)skb_push(skb, hl);
|
||||
fddi->fc = FDDI_FC_K_ASYNC_LLC_DEF;
|
||||
if(type == ETH_P_IP || type == ETH_P_IPV6 || type == ETH_P_ARP)
|
||||
{
|
||||
fddi->hdr.llc_snap.dsap = FDDI_EXTENDED_SAP;
|
||||
fddi->hdr.llc_snap.ssap = FDDI_EXTENDED_SAP;
|
||||
fddi->hdr.llc_snap.ctrl = FDDI_UI_CMD;
|
||||
fddi->hdr.llc_snap.oui[0] = 0x00;
|
||||
fddi->hdr.llc_snap.oui[1] = 0x00;
|
||||
fddi->hdr.llc_snap.oui[2] = 0x00;
|
||||
fddi->hdr.llc_snap.ethertype = htons(type);
|
||||
}
|
||||
|
||||
/* Set the source and destination hardware addresses */
|
||||
|
||||
if (saddr != NULL)
|
||||
memcpy(fddi->saddr, saddr, dev->addr_len);
|
||||
else
|
||||
memcpy(fddi->saddr, dev->dev_addr, dev->addr_len);
|
||||
|
||||
if (daddr != NULL)
|
||||
{
|
||||
memcpy(fddi->daddr, daddr, dev->addr_len);
|
||||
return hl;
|
||||
}
|
||||
|
||||
return -hl;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Rebuild the FDDI MAC header. This is called after an ARP
|
||||
* (or in future other address resolution) has completed on
|
||||
* this sk_buff. We now let ARP fill in the other fields.
|
||||
*/
|
||||
|
||||
static int fddi_rebuild_header(struct sk_buff *skb)
|
||||
{
|
||||
struct fddihdr *fddi = (struct fddihdr *)skb->data;
|
||||
|
||||
#ifdef CONFIG_INET
|
||||
if (fddi->hdr.llc_snap.ethertype == htons(ETH_P_IP))
|
||||
/* Try to get ARP to resolve the header and fill destination address */
|
||||
return arp_find(fddi->daddr, skb);
|
||||
else
|
||||
#endif
|
||||
{
|
||||
printk("%s: Don't know how to resolve type %04X addresses.\n",
|
||||
skb->dev->name, ntohs(fddi->hdr.llc_snap.ethertype));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Determine the packet's protocol ID and fill in skb fields.
|
||||
* This routine is called before an incoming packet is passed
|
||||
* up. It's used to fill in specific skb fields and to set
|
||||
* the proper pointer to the start of packet data (skb->data).
|
||||
*/
|
||||
|
||||
__be16 fddi_type_trans(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct fddihdr *fddi = (struct fddihdr *)skb->data;
|
||||
__be16 type;
|
||||
|
||||
/*
|
||||
* Set mac.raw field to point to FC byte, set data field to point
|
||||
* to start of packet data. Assume 802.2 SNAP frames for now.
|
||||
*/
|
||||
|
||||
skb->dev = dev;
|
||||
skb_reset_mac_header(skb); /* point to frame control (FC) */
|
||||
|
||||
if(fddi->hdr.llc_8022_1.dsap==0xe0)
|
||||
{
|
||||
skb_pull(skb, FDDI_K_8022_HLEN-3);
|
||||
type = htons(ETH_P_802_2);
|
||||
}
|
||||
else
|
||||
{
|
||||
skb_pull(skb, FDDI_K_SNAP_HLEN); /* adjust for 21 byte header */
|
||||
type=fddi->hdr.llc_snap.ethertype;
|
||||
}
|
||||
|
||||
/* Set packet type based on destination address and flag settings */
|
||||
|
||||
if (*fddi->daddr & 0x01)
|
||||
{
|
||||
if (memcmp(fddi->daddr, dev->broadcast, FDDI_K_ALEN) == 0)
|
||||
skb->pkt_type = PACKET_BROADCAST;
|
||||
else
|
||||
skb->pkt_type = PACKET_MULTICAST;
|
||||
}
|
||||
|
||||
else if (dev->flags & IFF_PROMISC)
|
||||
{
|
||||
if (memcmp(fddi->daddr, dev->dev_addr, FDDI_K_ALEN))
|
||||
skb->pkt_type = PACKET_OTHERHOST;
|
||||
}
|
||||
|
||||
/* Assume 802.2 SNAP frames, for now */
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(fddi_type_trans);
|
||||
|
||||
int fddi_change_mtu(struct net_device *dev, int new_mtu)
|
||||
{
|
||||
if ((new_mtu < FDDI_K_SNAP_HLEN) || (new_mtu > FDDI_K_SNAP_DLEN))
|
||||
return -EINVAL;
|
||||
dev->mtu = new_mtu;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(fddi_change_mtu);
|
||||
|
||||
static const struct header_ops fddi_header_ops = {
|
||||
.create = fddi_header,
|
||||
.rebuild = fddi_rebuild_header,
|
||||
};
|
||||
|
||||
|
||||
static void fddi_setup(struct net_device *dev)
|
||||
{
|
||||
dev->header_ops = &fddi_header_ops;
|
||||
dev->type = ARPHRD_FDDI;
|
||||
dev->hard_header_len = FDDI_K_SNAP_HLEN+3; /* Assume 802.2 SNAP hdr len + 3 pad bytes */
|
||||
dev->mtu = FDDI_K_SNAP_DLEN; /* Assume max payload of 802.2 SNAP frame */
|
||||
dev->addr_len = FDDI_K_ALEN;
|
||||
dev->tx_queue_len = 100; /* Long queues on FDDI */
|
||||
dev->flags = IFF_BROADCAST | IFF_MULTICAST;
|
||||
|
||||
memset(dev->broadcast, 0xFF, FDDI_K_ALEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* alloc_fddidev - Register FDDI device
|
||||
* @sizeof_priv: Size of additional driver-private structure to be allocated
|
||||
* for this FDDI device
|
||||
*
|
||||
* Fill in the fields of the device structure with FDDI-generic values.
|
||||
*
|
||||
* Constructs a new net device, complete with a private data area of
|
||||
* size @sizeof_priv. A 32-byte (not bit) alignment is enforced for
|
||||
* this private data area.
|
||||
*/
|
||||
struct net_device *alloc_fddidev(int sizeof_priv)
|
||||
{
|
||||
return alloc_netdev(sizeof_priv, "fddi%d", NET_NAME_UNKNOWN,
|
||||
fddi_setup);
|
||||
}
|
||||
EXPORT_SYMBOL(alloc_fddidev);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
638
net/802/garp.c
Normal file
638
net/802/garp.c
Normal file
|
@ -0,0 +1,638 @@
|
|||
/*
|
||||
* IEEE 802.1D Generic Attribute Registration Protocol (GARP)
|
||||
*
|
||||
* Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <linux/llc.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <net/llc.h>
|
||||
#include <net/llc_pdu.h>
|
||||
#include <net/garp.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
static unsigned int garp_join_time __read_mostly = 200;
|
||||
module_param(garp_join_time, uint, 0644);
|
||||
MODULE_PARM_DESC(garp_join_time, "Join time in ms (default 200ms)");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static const struct garp_state_trans {
|
||||
u8 state;
|
||||
u8 action;
|
||||
} garp_applicant_state_table[GARP_APPLICANT_MAX + 1][GARP_EVENT_MAX + 1] = {
|
||||
[GARP_APPLICANT_VA] = {
|
||||
[GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_AA,
|
||||
.action = GARP_ACTION_S_JOIN_IN },
|
||||
[GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_AA },
|
||||
[GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VA },
|
||||
[GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VA },
|
||||
[GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VA },
|
||||
[GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP },
|
||||
[GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID },
|
||||
[GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_LA },
|
||||
},
|
||||
[GARP_APPLICANT_AA] = {
|
||||
[GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_QA,
|
||||
.action = GARP_ACTION_S_JOIN_IN },
|
||||
[GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QA },
|
||||
[GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VA },
|
||||
[GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VA },
|
||||
[GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VA },
|
||||
[GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP },
|
||||
[GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID },
|
||||
[GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_LA },
|
||||
},
|
||||
[GARP_APPLICANT_QA] = {
|
||||
[GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_INVALID },
|
||||
[GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QA },
|
||||
[GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VA },
|
||||
[GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VA },
|
||||
[GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VP },
|
||||
[GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP },
|
||||
[GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID },
|
||||
[GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_LA },
|
||||
},
|
||||
[GARP_APPLICANT_LA] = {
|
||||
[GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_VO,
|
||||
.action = GARP_ACTION_S_LEAVE_EMPTY },
|
||||
[GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_LA },
|
||||
[GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VO },
|
||||
[GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_LA },
|
||||
[GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_LA },
|
||||
[GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VO },
|
||||
[GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_VA },
|
||||
[GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_INVALID },
|
||||
},
|
||||
[GARP_APPLICANT_VP] = {
|
||||
[GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_AA,
|
||||
.action = GARP_ACTION_S_JOIN_IN },
|
||||
[GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_AP },
|
||||
[GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VP },
|
||||
[GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VP },
|
||||
[GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VP },
|
||||
[GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP },
|
||||
[GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID },
|
||||
[GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_VO },
|
||||
},
|
||||
[GARP_APPLICANT_AP] = {
|
||||
[GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_QA,
|
||||
.action = GARP_ACTION_S_JOIN_IN },
|
||||
[GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QP },
|
||||
[GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VP },
|
||||
[GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VP },
|
||||
[GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VP },
|
||||
[GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP },
|
||||
[GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID },
|
||||
[GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_AO },
|
||||
},
|
||||
[GARP_APPLICANT_QP] = {
|
||||
[GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_INVALID },
|
||||
[GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QP },
|
||||
[GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VP },
|
||||
[GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VP },
|
||||
[GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VP },
|
||||
[GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP },
|
||||
[GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID },
|
||||
[GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_QO },
|
||||
},
|
||||
[GARP_APPLICANT_VO] = {
|
||||
[GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_INVALID },
|
||||
[GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_AO },
|
||||
[GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VO },
|
||||
[GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VO },
|
||||
[GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VO },
|
||||
[GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VO },
|
||||
[GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_VP },
|
||||
[GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_INVALID },
|
||||
},
|
||||
[GARP_APPLICANT_AO] = {
|
||||
[GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_INVALID },
|
||||
[GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QO },
|
||||
[GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VO },
|
||||
[GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VO },
|
||||
[GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VO },
|
||||
[GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VO },
|
||||
[GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_AP },
|
||||
[GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_INVALID },
|
||||
},
|
||||
[GARP_APPLICANT_QO] = {
|
||||
[GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_INVALID },
|
||||
[GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QO },
|
||||
[GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VO },
|
||||
[GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VO },
|
||||
[GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VO },
|
||||
[GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VO },
|
||||
[GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_QP },
|
||||
[GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_INVALID },
|
||||
},
|
||||
};
|
||||
|
||||
static int garp_attr_cmp(const struct garp_attr *attr,
|
||||
const void *data, u8 len, u8 type)
|
||||
{
|
||||
if (attr->type != type)
|
||||
return attr->type - type;
|
||||
if (attr->dlen != len)
|
||||
return attr->dlen - len;
|
||||
return memcmp(attr->data, data, len);
|
||||
}
|
||||
|
||||
static struct garp_attr *garp_attr_lookup(const struct garp_applicant *app,
|
||||
const void *data, u8 len, u8 type)
|
||||
{
|
||||
struct rb_node *parent = app->gid.rb_node;
|
||||
struct garp_attr *attr;
|
||||
int d;
|
||||
|
||||
while (parent) {
|
||||
attr = rb_entry(parent, struct garp_attr, node);
|
||||
d = garp_attr_cmp(attr, data, len, type);
|
||||
if (d > 0)
|
||||
parent = parent->rb_left;
|
||||
else if (d < 0)
|
||||
parent = parent->rb_right;
|
||||
else
|
||||
return attr;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct garp_attr *garp_attr_create(struct garp_applicant *app,
|
||||
const void *data, u8 len, u8 type)
|
||||
{
|
||||
struct rb_node *parent = NULL, **p = &app->gid.rb_node;
|
||||
struct garp_attr *attr;
|
||||
int d;
|
||||
|
||||
while (*p) {
|
||||
parent = *p;
|
||||
attr = rb_entry(parent, struct garp_attr, node);
|
||||
d = garp_attr_cmp(attr, data, len, type);
|
||||
if (d > 0)
|
||||
p = &parent->rb_left;
|
||||
else if (d < 0)
|
||||
p = &parent->rb_right;
|
||||
else {
|
||||
/* The attribute already exists; re-use it. */
|
||||
return attr;
|
||||
}
|
||||
}
|
||||
attr = kmalloc(sizeof(*attr) + len, GFP_ATOMIC);
|
||||
if (!attr)
|
||||
return attr;
|
||||
attr->state = GARP_APPLICANT_VO;
|
||||
attr->type = type;
|
||||
attr->dlen = len;
|
||||
memcpy(attr->data, data, len);
|
||||
|
||||
rb_link_node(&attr->node, parent, p);
|
||||
rb_insert_color(&attr->node, &app->gid);
|
||||
return attr;
|
||||
}
|
||||
|
||||
static void garp_attr_destroy(struct garp_applicant *app, struct garp_attr *attr)
|
||||
{
|
||||
rb_erase(&attr->node, &app->gid);
|
||||
kfree(attr);
|
||||
}
|
||||
|
||||
static int garp_pdu_init(struct garp_applicant *app)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct garp_pdu_hdr *gp;
|
||||
|
||||
#define LLC_RESERVE sizeof(struct llc_pdu_un)
|
||||
skb = alloc_skb(app->dev->mtu + LL_RESERVED_SPACE(app->dev),
|
||||
GFP_ATOMIC);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
skb->dev = app->dev;
|
||||
skb->protocol = htons(ETH_P_802_2);
|
||||
skb_reserve(skb, LL_RESERVED_SPACE(app->dev) + LLC_RESERVE);
|
||||
|
||||
gp = (struct garp_pdu_hdr *)__skb_put(skb, sizeof(*gp));
|
||||
put_unaligned(htons(GARP_PROTOCOL_ID), &gp->protocol);
|
||||
|
||||
app->pdu = skb;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int garp_pdu_append_end_mark(struct garp_applicant *app)
|
||||
{
|
||||
if (skb_tailroom(app->pdu) < sizeof(u8))
|
||||
return -1;
|
||||
*(u8 *)__skb_put(app->pdu, sizeof(u8)) = GARP_END_MARK;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void garp_pdu_queue(struct garp_applicant *app)
|
||||
{
|
||||
if (!app->pdu)
|
||||
return;
|
||||
|
||||
garp_pdu_append_end_mark(app);
|
||||
garp_pdu_append_end_mark(app);
|
||||
|
||||
llc_pdu_header_init(app->pdu, LLC_PDU_TYPE_U, LLC_SAP_BSPAN,
|
||||
LLC_SAP_BSPAN, LLC_PDU_CMD);
|
||||
llc_pdu_init_as_ui_cmd(app->pdu);
|
||||
llc_mac_hdr_init(app->pdu, app->dev->dev_addr,
|
||||
app->app->proto.group_address);
|
||||
|
||||
skb_queue_tail(&app->queue, app->pdu);
|
||||
app->pdu = NULL;
|
||||
}
|
||||
|
||||
static void garp_queue_xmit(struct garp_applicant *app)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
while ((skb = skb_dequeue(&app->queue)))
|
||||
dev_queue_xmit(skb);
|
||||
}
|
||||
|
||||
static int garp_pdu_append_msg(struct garp_applicant *app, u8 attrtype)
|
||||
{
|
||||
struct garp_msg_hdr *gm;
|
||||
|
||||
if (skb_tailroom(app->pdu) < sizeof(*gm))
|
||||
return -1;
|
||||
gm = (struct garp_msg_hdr *)__skb_put(app->pdu, sizeof(*gm));
|
||||
gm->attrtype = attrtype;
|
||||
garp_cb(app->pdu)->cur_type = attrtype;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int garp_pdu_append_attr(struct garp_applicant *app,
|
||||
const struct garp_attr *attr,
|
||||
enum garp_attr_event event)
|
||||
{
|
||||
struct garp_attr_hdr *ga;
|
||||
unsigned int len;
|
||||
int err;
|
||||
again:
|
||||
if (!app->pdu) {
|
||||
err = garp_pdu_init(app);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (garp_cb(app->pdu)->cur_type != attr->type) {
|
||||
if (garp_cb(app->pdu)->cur_type &&
|
||||
garp_pdu_append_end_mark(app) < 0)
|
||||
goto queue;
|
||||
if (garp_pdu_append_msg(app, attr->type) < 0)
|
||||
goto queue;
|
||||
}
|
||||
|
||||
len = sizeof(*ga) + attr->dlen;
|
||||
if (skb_tailroom(app->pdu) < len)
|
||||
goto queue;
|
||||
ga = (struct garp_attr_hdr *)__skb_put(app->pdu, len);
|
||||
ga->len = len;
|
||||
ga->event = event;
|
||||
memcpy(ga->data, attr->data, attr->dlen);
|
||||
return 0;
|
||||
|
||||
queue:
|
||||
garp_pdu_queue(app);
|
||||
goto again;
|
||||
}
|
||||
|
||||
static void garp_attr_event(struct garp_applicant *app,
|
||||
struct garp_attr *attr, enum garp_event event)
|
||||
{
|
||||
enum garp_applicant_state state;
|
||||
|
||||
state = garp_applicant_state_table[attr->state][event].state;
|
||||
if (state == GARP_APPLICANT_INVALID)
|
||||
return;
|
||||
|
||||
switch (garp_applicant_state_table[attr->state][event].action) {
|
||||
case GARP_ACTION_NONE:
|
||||
break;
|
||||
case GARP_ACTION_S_JOIN_IN:
|
||||
/* When appending the attribute fails, don't update state in
|
||||
* order to retry on next TRANSMIT_PDU event. */
|
||||
if (garp_pdu_append_attr(app, attr, GARP_JOIN_IN) < 0)
|
||||
return;
|
||||
break;
|
||||
case GARP_ACTION_S_LEAVE_EMPTY:
|
||||
garp_pdu_append_attr(app, attr, GARP_LEAVE_EMPTY);
|
||||
/* As a pure applicant, sending a leave message implies that
|
||||
* the attribute was unregistered and can be destroyed. */
|
||||
garp_attr_destroy(app, attr);
|
||||
return;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
}
|
||||
|
||||
attr->state = state;
|
||||
}
|
||||
|
||||
int garp_request_join(const struct net_device *dev,
|
||||
const struct garp_application *appl,
|
||||
const void *data, u8 len, u8 type)
|
||||
{
|
||||
struct garp_port *port = rtnl_dereference(dev->garp_port);
|
||||
struct garp_applicant *app = rtnl_dereference(port->applicants[appl->type]);
|
||||
struct garp_attr *attr;
|
||||
|
||||
spin_lock_bh(&app->lock);
|
||||
attr = garp_attr_create(app, data, len, type);
|
||||
if (!attr) {
|
||||
spin_unlock_bh(&app->lock);
|
||||
return -ENOMEM;
|
||||
}
|
||||
garp_attr_event(app, attr, GARP_EVENT_REQ_JOIN);
|
||||
spin_unlock_bh(&app->lock);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(garp_request_join);
|
||||
|
||||
void garp_request_leave(const struct net_device *dev,
|
||||
const struct garp_application *appl,
|
||||
const void *data, u8 len, u8 type)
|
||||
{
|
||||
struct garp_port *port = rtnl_dereference(dev->garp_port);
|
||||
struct garp_applicant *app = rtnl_dereference(port->applicants[appl->type]);
|
||||
struct garp_attr *attr;
|
||||
|
||||
spin_lock_bh(&app->lock);
|
||||
attr = garp_attr_lookup(app, data, len, type);
|
||||
if (!attr) {
|
||||
spin_unlock_bh(&app->lock);
|
||||
return;
|
||||
}
|
||||
garp_attr_event(app, attr, GARP_EVENT_REQ_LEAVE);
|
||||
spin_unlock_bh(&app->lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(garp_request_leave);
|
||||
|
||||
static void garp_gid_event(struct garp_applicant *app, enum garp_event event)
|
||||
{
|
||||
struct rb_node *node, *next;
|
||||
struct garp_attr *attr;
|
||||
|
||||
for (node = rb_first(&app->gid);
|
||||
next = node ? rb_next(node) : NULL, node != NULL;
|
||||
node = next) {
|
||||
attr = rb_entry(node, struct garp_attr, node);
|
||||
garp_attr_event(app, attr, event);
|
||||
}
|
||||
}
|
||||
|
||||
static void garp_join_timer_arm(struct garp_applicant *app)
|
||||
{
|
||||
unsigned long delay;
|
||||
|
||||
delay = (u64)msecs_to_jiffies(garp_join_time) * prandom_u32() >> 32;
|
||||
mod_timer(&app->join_timer, jiffies + delay);
|
||||
}
|
||||
|
||||
static void garp_join_timer(unsigned long data)
|
||||
{
|
||||
struct garp_applicant *app = (struct garp_applicant *)data;
|
||||
|
||||
spin_lock(&app->lock);
|
||||
garp_gid_event(app, GARP_EVENT_TRANSMIT_PDU);
|
||||
garp_pdu_queue(app);
|
||||
spin_unlock(&app->lock);
|
||||
|
||||
garp_queue_xmit(app);
|
||||
garp_join_timer_arm(app);
|
||||
}
|
||||
|
||||
static int garp_pdu_parse_end_mark(struct sk_buff *skb)
|
||||
{
|
||||
if (!pskb_may_pull(skb, sizeof(u8)))
|
||||
return -1;
|
||||
if (*skb->data == GARP_END_MARK) {
|
||||
skb_pull(skb, sizeof(u8));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int garp_pdu_parse_attr(struct garp_applicant *app, struct sk_buff *skb,
|
||||
u8 attrtype)
|
||||
{
|
||||
const struct garp_attr_hdr *ga;
|
||||
struct garp_attr *attr;
|
||||
enum garp_event event;
|
||||
unsigned int dlen;
|
||||
|
||||
if (!pskb_may_pull(skb, sizeof(*ga)))
|
||||
return -1;
|
||||
ga = (struct garp_attr_hdr *)skb->data;
|
||||
if (ga->len < sizeof(*ga))
|
||||
return -1;
|
||||
|
||||
if (!pskb_may_pull(skb, ga->len))
|
||||
return -1;
|
||||
skb_pull(skb, ga->len);
|
||||
dlen = sizeof(*ga) - ga->len;
|
||||
|
||||
if (attrtype > app->app->maxattr)
|
||||
return 0;
|
||||
|
||||
switch (ga->event) {
|
||||
case GARP_LEAVE_ALL:
|
||||
if (dlen != 0)
|
||||
return -1;
|
||||
garp_gid_event(app, GARP_EVENT_R_LEAVE_EMPTY);
|
||||
return 0;
|
||||
case GARP_JOIN_EMPTY:
|
||||
event = GARP_EVENT_R_JOIN_EMPTY;
|
||||
break;
|
||||
case GARP_JOIN_IN:
|
||||
event = GARP_EVENT_R_JOIN_IN;
|
||||
break;
|
||||
case GARP_LEAVE_EMPTY:
|
||||
event = GARP_EVENT_R_LEAVE_EMPTY;
|
||||
break;
|
||||
case GARP_EMPTY:
|
||||
event = GARP_EVENT_R_EMPTY;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (dlen == 0)
|
||||
return -1;
|
||||
attr = garp_attr_lookup(app, ga->data, dlen, attrtype);
|
||||
if (attr == NULL)
|
||||
return 0;
|
||||
garp_attr_event(app, attr, event);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int garp_pdu_parse_msg(struct garp_applicant *app, struct sk_buff *skb)
|
||||
{
|
||||
const struct garp_msg_hdr *gm;
|
||||
|
||||
if (!pskb_may_pull(skb, sizeof(*gm)))
|
||||
return -1;
|
||||
gm = (struct garp_msg_hdr *)skb->data;
|
||||
if (gm->attrtype == 0)
|
||||
return -1;
|
||||
skb_pull(skb, sizeof(*gm));
|
||||
|
||||
while (skb->len > 0) {
|
||||
if (garp_pdu_parse_attr(app, skb, gm->attrtype) < 0)
|
||||
return -1;
|
||||
if (garp_pdu_parse_end_mark(skb) < 0)
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void garp_pdu_rcv(const struct stp_proto *proto, struct sk_buff *skb,
|
||||
struct net_device *dev)
|
||||
{
|
||||
struct garp_application *appl = proto->data;
|
||||
struct garp_port *port;
|
||||
struct garp_applicant *app;
|
||||
const struct garp_pdu_hdr *gp;
|
||||
|
||||
port = rcu_dereference(dev->garp_port);
|
||||
if (!port)
|
||||
goto err;
|
||||
app = rcu_dereference(port->applicants[appl->type]);
|
||||
if (!app)
|
||||
goto err;
|
||||
|
||||
if (!pskb_may_pull(skb, sizeof(*gp)))
|
||||
goto err;
|
||||
gp = (struct garp_pdu_hdr *)skb->data;
|
||||
if (get_unaligned(&gp->protocol) != htons(GARP_PROTOCOL_ID))
|
||||
goto err;
|
||||
skb_pull(skb, sizeof(*gp));
|
||||
|
||||
spin_lock(&app->lock);
|
||||
while (skb->len > 0) {
|
||||
if (garp_pdu_parse_msg(app, skb) < 0)
|
||||
break;
|
||||
if (garp_pdu_parse_end_mark(skb) < 0)
|
||||
break;
|
||||
}
|
||||
spin_unlock(&app->lock);
|
||||
err:
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
static int garp_init_port(struct net_device *dev)
|
||||
{
|
||||
struct garp_port *port;
|
||||
|
||||
port = kzalloc(sizeof(*port), GFP_KERNEL);
|
||||
if (!port)
|
||||
return -ENOMEM;
|
||||
rcu_assign_pointer(dev->garp_port, port);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void garp_release_port(struct net_device *dev)
|
||||
{
|
||||
struct garp_port *port = rtnl_dereference(dev->garp_port);
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i <= GARP_APPLICATION_MAX; i++) {
|
||||
if (rtnl_dereference(port->applicants[i]))
|
||||
return;
|
||||
}
|
||||
RCU_INIT_POINTER(dev->garp_port, NULL);
|
||||
kfree_rcu(port, rcu);
|
||||
}
|
||||
|
||||
int garp_init_applicant(struct net_device *dev, struct garp_application *appl)
|
||||
{
|
||||
struct garp_applicant *app;
|
||||
int err;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
if (!rtnl_dereference(dev->garp_port)) {
|
||||
err = garp_init_port(dev);
|
||||
if (err < 0)
|
||||
goto err1;
|
||||
}
|
||||
|
||||
err = -ENOMEM;
|
||||
app = kzalloc(sizeof(*app), GFP_KERNEL);
|
||||
if (!app)
|
||||
goto err2;
|
||||
|
||||
err = dev_mc_add(dev, appl->proto.group_address);
|
||||
if (err < 0)
|
||||
goto err3;
|
||||
|
||||
app->dev = dev;
|
||||
app->app = appl;
|
||||
app->gid = RB_ROOT;
|
||||
spin_lock_init(&app->lock);
|
||||
skb_queue_head_init(&app->queue);
|
||||
rcu_assign_pointer(dev->garp_port->applicants[appl->type], app);
|
||||
setup_timer(&app->join_timer, garp_join_timer, (unsigned long)app);
|
||||
garp_join_timer_arm(app);
|
||||
return 0;
|
||||
|
||||
err3:
|
||||
kfree(app);
|
||||
err2:
|
||||
garp_release_port(dev);
|
||||
err1:
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(garp_init_applicant);
|
||||
|
||||
void garp_uninit_applicant(struct net_device *dev, struct garp_application *appl)
|
||||
{
|
||||
struct garp_port *port = rtnl_dereference(dev->garp_port);
|
||||
struct garp_applicant *app = rtnl_dereference(port->applicants[appl->type]);
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
RCU_INIT_POINTER(port->applicants[appl->type], NULL);
|
||||
|
||||
/* Delete timer and generate a final TRANSMIT_PDU event to flush out
|
||||
* all pending messages before the applicant is gone. */
|
||||
del_timer_sync(&app->join_timer);
|
||||
|
||||
spin_lock_bh(&app->lock);
|
||||
garp_gid_event(app, GARP_EVENT_TRANSMIT_PDU);
|
||||
garp_pdu_queue(app);
|
||||
spin_unlock_bh(&app->lock);
|
||||
|
||||
garp_queue_xmit(app);
|
||||
|
||||
dev_mc_del(dev, appl->proto.group_address);
|
||||
kfree_rcu(app, rcu);
|
||||
garp_release_port(dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(garp_uninit_applicant);
|
||||
|
||||
int garp_register_application(struct garp_application *appl)
|
||||
{
|
||||
appl->proto.rcv = garp_pdu_rcv;
|
||||
appl->proto.data = appl;
|
||||
return stp_proto_register(&appl->proto);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(garp_register_application);
|
||||
|
||||
void garp_unregister_application(struct garp_application *appl)
|
||||
{
|
||||
stp_proto_unregister(&appl->proto);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(garp_unregister_application);
|
235
net/802/hippi.c
Normal file
235
net/802/hippi.c
Normal file
|
@ -0,0 +1,235 @@
|
|||
/*
|
||||
* INET An implementation of the TCP/IP protocol suite for the LINUX
|
||||
* operating system. INET is implemented using the BSD Socket
|
||||
* interface as the means of communication with the user level.
|
||||
*
|
||||
* HIPPI-type device handling.
|
||||
*
|
||||
* Version: @(#)hippi.c 1.0.0 05/29/97
|
||||
*
|
||||
* Authors: Ross Biro
|
||||
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
|
||||
* Mark Evans, <evansmp@uhura.aston.ac.uk>
|
||||
* Florian La Roche, <rzsfl@rz.uni-sb.de>
|
||||
* Alan Cox, <gw4pts@gw4pts.ampr.org>
|
||||
* Jes Sorensen, <Jes.Sorensen@cern.ch>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/inet.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/hippidevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/errno.h>
|
||||
#include <net/arp.h>
|
||||
#include <net/sock.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
/*
|
||||
* Create the HIPPI MAC header for an arbitrary protocol layer
|
||||
*
|
||||
* saddr=NULL means use device source address
|
||||
* daddr=NULL means leave destination address (eg unresolved arp)
|
||||
*/
|
||||
|
||||
static int hippi_header(struct sk_buff *skb, struct net_device *dev,
|
||||
unsigned short type,
|
||||
const void *daddr, const void *saddr, unsigned int len)
|
||||
{
|
||||
struct hippi_hdr *hip = (struct hippi_hdr *)skb_push(skb, HIPPI_HLEN);
|
||||
struct hippi_cb *hcb = (struct hippi_cb *) skb->cb;
|
||||
|
||||
if (!len){
|
||||
len = skb->len - HIPPI_HLEN;
|
||||
printk("hippi_header(): length not supplied\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Due to the stupidity of the little endian byte-order we
|
||||
* have to set the fp field this way.
|
||||
*/
|
||||
hip->fp.fixed = htonl(0x04800018);
|
||||
hip->fp.d2_size = htonl(len + 8);
|
||||
hip->le.fc = 0;
|
||||
hip->le.double_wide = 0; /* only HIPPI 800 for the time being */
|
||||
hip->le.message_type = 0; /* Data PDU */
|
||||
|
||||
hip->le.dest_addr_type = 2; /* 12 bit SC address */
|
||||
hip->le.src_addr_type = 2; /* 12 bit SC address */
|
||||
|
||||
memcpy(hip->le.src_switch_addr, dev->dev_addr + 3, 3);
|
||||
memset(&hip->le.reserved, 0, 16);
|
||||
|
||||
hip->snap.dsap = HIPPI_EXTENDED_SAP;
|
||||
hip->snap.ssap = HIPPI_EXTENDED_SAP;
|
||||
hip->snap.ctrl = HIPPI_UI_CMD;
|
||||
hip->snap.oui[0] = 0x00;
|
||||
hip->snap.oui[1] = 0x00;
|
||||
hip->snap.oui[2] = 0x00;
|
||||
hip->snap.ethertype = htons(type);
|
||||
|
||||
if (daddr)
|
||||
{
|
||||
memcpy(hip->le.dest_switch_addr, daddr + 3, 3);
|
||||
memcpy(&hcb->ifield, daddr + 2, 4);
|
||||
return HIPPI_HLEN;
|
||||
}
|
||||
hcb->ifield = 0;
|
||||
return -((int)HIPPI_HLEN);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Rebuild the HIPPI MAC header. This is called after an ARP has
|
||||
* completed on this sk_buff. We now let ARP fill in the other fields.
|
||||
*/
|
||||
|
||||
static int hippi_rebuild_header(struct sk_buff *skb)
|
||||
{
|
||||
struct hippi_hdr *hip = (struct hippi_hdr *)skb->data;
|
||||
|
||||
/*
|
||||
* Only IP is currently supported
|
||||
*/
|
||||
|
||||
if(hip->snap.ethertype != htons(ETH_P_IP))
|
||||
{
|
||||
printk(KERN_DEBUG "%s: unable to resolve type %X addresses.\n",skb->dev->name,ntohs(hip->snap.ethertype));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We don't support dynamic ARP on HIPPI, but we use the ARP
|
||||
* static ARP tables to hold the I-FIELDs.
|
||||
*/
|
||||
return arp_find(hip->le.daddr, skb);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Determine the packet's protocol ID.
|
||||
*/
|
||||
|
||||
__be16 hippi_type_trans(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct hippi_hdr *hip;
|
||||
|
||||
/*
|
||||
* This is actually wrong ... question is if we really should
|
||||
* set the raw address here.
|
||||
*/
|
||||
skb->dev = dev;
|
||||
skb_reset_mac_header(skb);
|
||||
hip = (struct hippi_hdr *)skb_mac_header(skb);
|
||||
skb_pull(skb, HIPPI_HLEN);
|
||||
|
||||
/*
|
||||
* No fancy promisc stuff here now.
|
||||
*/
|
||||
|
||||
return hip->snap.ethertype;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(hippi_type_trans);
|
||||
|
||||
int hippi_change_mtu(struct net_device *dev, int new_mtu)
|
||||
{
|
||||
/*
|
||||
* HIPPI's got these nice large MTUs.
|
||||
*/
|
||||
if ((new_mtu < 68) || (new_mtu > 65280))
|
||||
return -EINVAL;
|
||||
dev->mtu = new_mtu;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(hippi_change_mtu);
|
||||
|
||||
/*
|
||||
* For HIPPI we will actually use the lower 4 bytes of the hardware
|
||||
* address as the I-FIELD rather than the actual hardware address.
|
||||
*/
|
||||
int hippi_mac_addr(struct net_device *dev, void *p)
|
||||
{
|
||||
struct sockaddr *addr = p;
|
||||
if (netif_running(dev))
|
||||
return -EBUSY;
|
||||
memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(hippi_mac_addr);
|
||||
|
||||
int hippi_neigh_setup_dev(struct net_device *dev, struct neigh_parms *p)
|
||||
{
|
||||
/* Never send broadcast/multicast ARP messages */
|
||||
NEIGH_VAR_INIT(p, MCAST_PROBES, 0);
|
||||
|
||||
/* In IPv6 unicast probes are valid even on NBMA,
|
||||
* because they are encapsulated in normal IPv6 protocol.
|
||||
* Should be a generic flag.
|
||||
*/
|
||||
if (p->tbl->family != AF_INET6)
|
||||
NEIGH_VAR_INIT(p, UCAST_PROBES, 0);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(hippi_neigh_setup_dev);
|
||||
|
||||
static const struct header_ops hippi_header_ops = {
|
||||
.create = hippi_header,
|
||||
.rebuild = hippi_rebuild_header,
|
||||
};
|
||||
|
||||
|
||||
static void hippi_setup(struct net_device *dev)
|
||||
{
|
||||
dev->header_ops = &hippi_header_ops;
|
||||
|
||||
/*
|
||||
* We don't support HIPPI `ARP' for the time being, and probably
|
||||
* never will unless someone else implements it. However we
|
||||
* still need a fake ARPHRD to make ifconfig and friends play ball.
|
||||
*/
|
||||
dev->type = ARPHRD_HIPPI;
|
||||
dev->hard_header_len = HIPPI_HLEN;
|
||||
dev->mtu = 65280;
|
||||
dev->addr_len = HIPPI_ALEN;
|
||||
dev->tx_queue_len = 25 /* 5 */;
|
||||
memset(dev->broadcast, 0xFF, HIPPI_ALEN);
|
||||
|
||||
|
||||
/*
|
||||
* HIPPI doesn't support broadcast+multicast and we only use
|
||||
* static ARP tables. ARP is disabled by hippi_neigh_setup_dev.
|
||||
*/
|
||||
dev->flags = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* alloc_hippi_dev - Register HIPPI device
|
||||
* @sizeof_priv: Size of additional driver-private structure to be allocated
|
||||
* for this HIPPI device
|
||||
*
|
||||
* Fill in the fields of the device structure with HIPPI-generic values.
|
||||
*
|
||||
* Constructs a new net device, complete with a private data area of
|
||||
* size @sizeof_priv. A 32-byte (not bit) alignment is enforced for
|
||||
* this private data area.
|
||||
*/
|
||||
|
||||
struct net_device *alloc_hippi_dev(int sizeof_priv)
|
||||
{
|
||||
return alloc_netdev(sizeof_priv, "hip%d", NET_NAME_UNKNOWN,
|
||||
hippi_setup);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(alloc_hippi_dev);
|
926
net/802/mrp.c
Normal file
926
net/802/mrp.c
Normal file
|
@ -0,0 +1,926 @@
|
|||
/*
|
||||
* IEEE 802.1Q Multiple Registration Protocol (MRP)
|
||||
*
|
||||
* Copyright (c) 2012 Massachusetts Institute of Technology
|
||||
*
|
||||
* Adapted from code in net/802/garp.c
|
||||
* Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <net/mrp.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
static unsigned int mrp_join_time __read_mostly = 200;
|
||||
module_param(mrp_join_time, uint, 0644);
|
||||
MODULE_PARM_DESC(mrp_join_time, "Join time in ms (default 200ms)");
|
||||
|
||||
static unsigned int mrp_periodic_time __read_mostly = 1000;
|
||||
module_param(mrp_periodic_time, uint, 0644);
|
||||
MODULE_PARM_DESC(mrp_periodic_time, "Periodic time in ms (default 1s)");
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static const u8
|
||||
mrp_applicant_state_table[MRP_APPLICANT_MAX + 1][MRP_EVENT_MAX + 1] = {
|
||||
[MRP_APPLICANT_VO] = {
|
||||
[MRP_EVENT_NEW] = MRP_APPLICANT_VN,
|
||||
[MRP_EVENT_JOIN] = MRP_APPLICANT_VP,
|
||||
[MRP_EVENT_LV] = MRP_APPLICANT_VO,
|
||||
[MRP_EVENT_TX] = MRP_APPLICANT_VO,
|
||||
[MRP_EVENT_R_NEW] = MRP_APPLICANT_VO,
|
||||
[MRP_EVENT_R_JOIN_IN] = MRP_APPLICANT_AO,
|
||||
[MRP_EVENT_R_IN] = MRP_APPLICANT_VO,
|
||||
[MRP_EVENT_R_JOIN_MT] = MRP_APPLICANT_VO,
|
||||
[MRP_EVENT_R_MT] = MRP_APPLICANT_VO,
|
||||
[MRP_EVENT_R_LV] = MRP_APPLICANT_VO,
|
||||
[MRP_EVENT_R_LA] = MRP_APPLICANT_VO,
|
||||
[MRP_EVENT_REDECLARE] = MRP_APPLICANT_VO,
|
||||
[MRP_EVENT_PERIODIC] = MRP_APPLICANT_VO,
|
||||
},
|
||||
[MRP_APPLICANT_VP] = {
|
||||
[MRP_EVENT_NEW] = MRP_APPLICANT_VN,
|
||||
[MRP_EVENT_JOIN] = MRP_APPLICANT_VP,
|
||||
[MRP_EVENT_LV] = MRP_APPLICANT_VO,
|
||||
[MRP_EVENT_TX] = MRP_APPLICANT_AA,
|
||||
[MRP_EVENT_R_NEW] = MRP_APPLICANT_VP,
|
||||
[MRP_EVENT_R_JOIN_IN] = MRP_APPLICANT_AP,
|
||||
[MRP_EVENT_R_IN] = MRP_APPLICANT_VP,
|
||||
[MRP_EVENT_R_JOIN_MT] = MRP_APPLICANT_VP,
|
||||
[MRP_EVENT_R_MT] = MRP_APPLICANT_VP,
|
||||
[MRP_EVENT_R_LV] = MRP_APPLICANT_VP,
|
||||
[MRP_EVENT_R_LA] = MRP_APPLICANT_VP,
|
||||
[MRP_EVENT_REDECLARE] = MRP_APPLICANT_VP,
|
||||
[MRP_EVENT_PERIODIC] = MRP_APPLICANT_VP,
|
||||
},
|
||||
[MRP_APPLICANT_VN] = {
|
||||
[MRP_EVENT_NEW] = MRP_APPLICANT_VN,
|
||||
[MRP_EVENT_JOIN] = MRP_APPLICANT_VN,
|
||||
[MRP_EVENT_LV] = MRP_APPLICANT_LA,
|
||||
[MRP_EVENT_TX] = MRP_APPLICANT_AN,
|
||||
[MRP_EVENT_R_NEW] = MRP_APPLICANT_VN,
|
||||
[MRP_EVENT_R_JOIN_IN] = MRP_APPLICANT_VN,
|
||||
[MRP_EVENT_R_IN] = MRP_APPLICANT_VN,
|
||||
[MRP_EVENT_R_JOIN_MT] = MRP_APPLICANT_VN,
|
||||
[MRP_EVENT_R_MT] = MRP_APPLICANT_VN,
|
||||
[MRP_EVENT_R_LV] = MRP_APPLICANT_VN,
|
||||
[MRP_EVENT_R_LA] = MRP_APPLICANT_VN,
|
||||
[MRP_EVENT_REDECLARE] = MRP_APPLICANT_VN,
|
||||
[MRP_EVENT_PERIODIC] = MRP_APPLICANT_VN,
|
||||
},
|
||||
[MRP_APPLICANT_AN] = {
|
||||
[MRP_EVENT_NEW] = MRP_APPLICANT_AN,
|
||||
[MRP_EVENT_JOIN] = MRP_APPLICANT_AN,
|
||||
[MRP_EVENT_LV] = MRP_APPLICANT_LA,
|
||||
[MRP_EVENT_TX] = MRP_APPLICANT_QA,
|
||||
[MRP_EVENT_R_NEW] = MRP_APPLICANT_AN,
|
||||
[MRP_EVENT_R_JOIN_IN] = MRP_APPLICANT_AN,
|
||||
[MRP_EVENT_R_IN] = MRP_APPLICANT_AN,
|
||||
[MRP_EVENT_R_JOIN_MT] = MRP_APPLICANT_AN,
|
||||
[MRP_EVENT_R_MT] = MRP_APPLICANT_AN,
|
||||
[MRP_EVENT_R_LV] = MRP_APPLICANT_VN,
|
||||
[MRP_EVENT_R_LA] = MRP_APPLICANT_VN,
|
||||
[MRP_EVENT_REDECLARE] = MRP_APPLICANT_VN,
|
||||
[MRP_EVENT_PERIODIC] = MRP_APPLICANT_AN,
|
||||
},
|
||||
[MRP_APPLICANT_AA] = {
|
||||
[MRP_EVENT_NEW] = MRP_APPLICANT_VN,
|
||||
[MRP_EVENT_JOIN] = MRP_APPLICANT_AA,
|
||||
[MRP_EVENT_LV] = MRP_APPLICANT_LA,
|
||||
[MRP_EVENT_TX] = MRP_APPLICANT_QA,
|
||||
[MRP_EVENT_R_NEW] = MRP_APPLICANT_AA,
|
||||
[MRP_EVENT_R_JOIN_IN] = MRP_APPLICANT_QA,
|
||||
[MRP_EVENT_R_IN] = MRP_APPLICANT_AA,
|
||||
[MRP_EVENT_R_JOIN_MT] = MRP_APPLICANT_AA,
|
||||
[MRP_EVENT_R_MT] = MRP_APPLICANT_AA,
|
||||
[MRP_EVENT_R_LV] = MRP_APPLICANT_VP,
|
||||
[MRP_EVENT_R_LA] = MRP_APPLICANT_VP,
|
||||
[MRP_EVENT_REDECLARE] = MRP_APPLICANT_VP,
|
||||
[MRP_EVENT_PERIODIC] = MRP_APPLICANT_AA,
|
||||
},
|
||||
[MRP_APPLICANT_QA] = {
|
||||
[MRP_EVENT_NEW] = MRP_APPLICANT_VN,
|
||||
[MRP_EVENT_JOIN] = MRP_APPLICANT_QA,
|
||||
[MRP_EVENT_LV] = MRP_APPLICANT_LA,
|
||||
[MRP_EVENT_TX] = MRP_APPLICANT_QA,
|
||||
[MRP_EVENT_R_NEW] = MRP_APPLICANT_QA,
|
||||
[MRP_EVENT_R_JOIN_IN] = MRP_APPLICANT_QA,
|
||||
[MRP_EVENT_R_IN] = MRP_APPLICANT_QA,
|
||||
[MRP_EVENT_R_JOIN_MT] = MRP_APPLICANT_AA,
|
||||
[MRP_EVENT_R_MT] = MRP_APPLICANT_AA,
|
||||
[MRP_EVENT_R_LV] = MRP_APPLICANT_VP,
|
||||
[MRP_EVENT_R_LA] = MRP_APPLICANT_VP,
|
||||
[MRP_EVENT_REDECLARE] = MRP_APPLICANT_VP,
|
||||
[MRP_EVENT_PERIODIC] = MRP_APPLICANT_AA,
|
||||
},
|
||||
[MRP_APPLICANT_LA] = {
|
||||
[MRP_EVENT_NEW] = MRP_APPLICANT_VN,
|
||||
[MRP_EVENT_JOIN] = MRP_APPLICANT_AA,
|
||||
[MRP_EVENT_LV] = MRP_APPLICANT_LA,
|
||||
[MRP_EVENT_TX] = MRP_APPLICANT_VO,
|
||||
[MRP_EVENT_R_NEW] = MRP_APPLICANT_LA,
|
||||
[MRP_EVENT_R_JOIN_IN] = MRP_APPLICANT_LA,
|
||||
[MRP_EVENT_R_IN] = MRP_APPLICANT_LA,
|
||||
[MRP_EVENT_R_JOIN_MT] = MRP_APPLICANT_LA,
|
||||
[MRP_EVENT_R_MT] = MRP_APPLICANT_LA,
|
||||
[MRP_EVENT_R_LV] = MRP_APPLICANT_LA,
|
||||
[MRP_EVENT_R_LA] = MRP_APPLICANT_LA,
|
||||
[MRP_EVENT_REDECLARE] = MRP_APPLICANT_LA,
|
||||
[MRP_EVENT_PERIODIC] = MRP_APPLICANT_LA,
|
||||
},
|
||||
[MRP_APPLICANT_AO] = {
|
||||
[MRP_EVENT_NEW] = MRP_APPLICANT_VN,
|
||||
[MRP_EVENT_JOIN] = MRP_APPLICANT_AP,
|
||||
[MRP_EVENT_LV] = MRP_APPLICANT_AO,
|
||||
[MRP_EVENT_TX] = MRP_APPLICANT_AO,
|
||||
[MRP_EVENT_R_NEW] = MRP_APPLICANT_AO,
|
||||
[MRP_EVENT_R_JOIN_IN] = MRP_APPLICANT_QO,
|
||||
[MRP_EVENT_R_IN] = MRP_APPLICANT_AO,
|
||||
[MRP_EVENT_R_JOIN_MT] = MRP_APPLICANT_AO,
|
||||
[MRP_EVENT_R_MT] = MRP_APPLICANT_AO,
|
||||
[MRP_EVENT_R_LV] = MRP_APPLICANT_VO,
|
||||
[MRP_EVENT_R_LA] = MRP_APPLICANT_VO,
|
||||
[MRP_EVENT_REDECLARE] = MRP_APPLICANT_VO,
|
||||
[MRP_EVENT_PERIODIC] = MRP_APPLICANT_AO,
|
||||
},
|
||||
[MRP_APPLICANT_QO] = {
|
||||
[MRP_EVENT_NEW] = MRP_APPLICANT_VN,
|
||||
[MRP_EVENT_JOIN] = MRP_APPLICANT_QP,
|
||||
[MRP_EVENT_LV] = MRP_APPLICANT_QO,
|
||||
[MRP_EVENT_TX] = MRP_APPLICANT_QO,
|
||||
[MRP_EVENT_R_NEW] = MRP_APPLICANT_QO,
|
||||
[MRP_EVENT_R_JOIN_IN] = MRP_APPLICANT_QO,
|
||||
[MRP_EVENT_R_IN] = MRP_APPLICANT_QO,
|
||||
[MRP_EVENT_R_JOIN_MT] = MRP_APPLICANT_AO,
|
||||
[MRP_EVENT_R_MT] = MRP_APPLICANT_AO,
|
||||
[MRP_EVENT_R_LV] = MRP_APPLICANT_VO,
|
||||
[MRP_EVENT_R_LA] = MRP_APPLICANT_VO,
|
||||
[MRP_EVENT_REDECLARE] = MRP_APPLICANT_VO,
|
||||
[MRP_EVENT_PERIODIC] = MRP_APPLICANT_QO,
|
||||
},
|
||||
[MRP_APPLICANT_AP] = {
|
||||
[MRP_EVENT_NEW] = MRP_APPLICANT_VN,
|
||||
[MRP_EVENT_JOIN] = MRP_APPLICANT_AP,
|
||||
[MRP_EVENT_LV] = MRP_APPLICANT_AO,
|
||||
[MRP_EVENT_TX] = MRP_APPLICANT_QA,
|
||||
[MRP_EVENT_R_NEW] = MRP_APPLICANT_AP,
|
||||
[MRP_EVENT_R_JOIN_IN] = MRP_APPLICANT_QP,
|
||||
[MRP_EVENT_R_IN] = MRP_APPLICANT_AP,
|
||||
[MRP_EVENT_R_JOIN_MT] = MRP_APPLICANT_AP,
|
||||
[MRP_EVENT_R_MT] = MRP_APPLICANT_AP,
|
||||
[MRP_EVENT_R_LV] = MRP_APPLICANT_VP,
|
||||
[MRP_EVENT_R_LA] = MRP_APPLICANT_VP,
|
||||
[MRP_EVENT_REDECLARE] = MRP_APPLICANT_VP,
|
||||
[MRP_EVENT_PERIODIC] = MRP_APPLICANT_AP,
|
||||
},
|
||||
[MRP_APPLICANT_QP] = {
|
||||
[MRP_EVENT_NEW] = MRP_APPLICANT_VN,
|
||||
[MRP_EVENT_JOIN] = MRP_APPLICANT_QP,
|
||||
[MRP_EVENT_LV] = MRP_APPLICANT_QO,
|
||||
[MRP_EVENT_TX] = MRP_APPLICANT_QP,
|
||||
[MRP_EVENT_R_NEW] = MRP_APPLICANT_QP,
|
||||
[MRP_EVENT_R_JOIN_IN] = MRP_APPLICANT_QP,
|
||||
[MRP_EVENT_R_IN] = MRP_APPLICANT_QP,
|
||||
[MRP_EVENT_R_JOIN_MT] = MRP_APPLICANT_AP,
|
||||
[MRP_EVENT_R_MT] = MRP_APPLICANT_AP,
|
||||
[MRP_EVENT_R_LV] = MRP_APPLICANT_VP,
|
||||
[MRP_EVENT_R_LA] = MRP_APPLICANT_VP,
|
||||
[MRP_EVENT_REDECLARE] = MRP_APPLICANT_VP,
|
||||
[MRP_EVENT_PERIODIC] = MRP_APPLICANT_AP,
|
||||
},
|
||||
};
|
||||
|
||||
static const u8
|
||||
mrp_tx_action_table[MRP_APPLICANT_MAX + 1] = {
|
||||
[MRP_APPLICANT_VO] = MRP_TX_ACTION_S_IN_OPTIONAL,
|
||||
[MRP_APPLICANT_VP] = MRP_TX_ACTION_S_JOIN_IN,
|
||||
[MRP_APPLICANT_VN] = MRP_TX_ACTION_S_NEW,
|
||||
[MRP_APPLICANT_AN] = MRP_TX_ACTION_S_NEW,
|
||||
[MRP_APPLICANT_AA] = MRP_TX_ACTION_S_JOIN_IN,
|
||||
[MRP_APPLICANT_QA] = MRP_TX_ACTION_S_JOIN_IN_OPTIONAL,
|
||||
[MRP_APPLICANT_LA] = MRP_TX_ACTION_S_LV,
|
||||
[MRP_APPLICANT_AO] = MRP_TX_ACTION_S_IN_OPTIONAL,
|
||||
[MRP_APPLICANT_QO] = MRP_TX_ACTION_S_IN_OPTIONAL,
|
||||
[MRP_APPLICANT_AP] = MRP_TX_ACTION_S_JOIN_IN,
|
||||
[MRP_APPLICANT_QP] = MRP_TX_ACTION_S_IN_OPTIONAL,
|
||||
};
|
||||
|
||||
static void mrp_attrvalue_inc(void *value, u8 len)
|
||||
{
|
||||
u8 *v = (u8 *)value;
|
||||
|
||||
/* Add 1 to the last byte. If it becomes zero,
|
||||
* go to the previous byte and repeat.
|
||||
*/
|
||||
while (len > 0 && !++v[--len])
|
||||
;
|
||||
}
|
||||
|
||||
static int mrp_attr_cmp(const struct mrp_attr *attr,
|
||||
const void *value, u8 len, u8 type)
|
||||
{
|
||||
if (attr->type != type)
|
||||
return attr->type - type;
|
||||
if (attr->len != len)
|
||||
return attr->len - len;
|
||||
return memcmp(attr->value, value, len);
|
||||
}
|
||||
|
||||
static struct mrp_attr *mrp_attr_lookup(const struct mrp_applicant *app,
|
||||
const void *value, u8 len, u8 type)
|
||||
{
|
||||
struct rb_node *parent = app->mad.rb_node;
|
||||
struct mrp_attr *attr;
|
||||
int d;
|
||||
|
||||
while (parent) {
|
||||
attr = rb_entry(parent, struct mrp_attr, node);
|
||||
d = mrp_attr_cmp(attr, value, len, type);
|
||||
if (d > 0)
|
||||
parent = parent->rb_left;
|
||||
else if (d < 0)
|
||||
parent = parent->rb_right;
|
||||
else
|
||||
return attr;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct mrp_attr *mrp_attr_create(struct mrp_applicant *app,
|
||||
const void *value, u8 len, u8 type)
|
||||
{
|
||||
struct rb_node *parent = NULL, **p = &app->mad.rb_node;
|
||||
struct mrp_attr *attr;
|
||||
int d;
|
||||
|
||||
while (*p) {
|
||||
parent = *p;
|
||||
attr = rb_entry(parent, struct mrp_attr, node);
|
||||
d = mrp_attr_cmp(attr, value, len, type);
|
||||
if (d > 0)
|
||||
p = &parent->rb_left;
|
||||
else if (d < 0)
|
||||
p = &parent->rb_right;
|
||||
else {
|
||||
/* The attribute already exists; re-use it. */
|
||||
return attr;
|
||||
}
|
||||
}
|
||||
attr = kmalloc(sizeof(*attr) + len, GFP_ATOMIC);
|
||||
if (!attr)
|
||||
return attr;
|
||||
attr->state = MRP_APPLICANT_VO;
|
||||
attr->type = type;
|
||||
attr->len = len;
|
||||
memcpy(attr->value, value, len);
|
||||
|
||||
rb_link_node(&attr->node, parent, p);
|
||||
rb_insert_color(&attr->node, &app->mad);
|
||||
return attr;
|
||||
}
|
||||
|
||||
static void mrp_attr_destroy(struct mrp_applicant *app, struct mrp_attr *attr)
|
||||
{
|
||||
rb_erase(&attr->node, &app->mad);
|
||||
kfree(attr);
|
||||
}
|
||||
|
||||
static int mrp_pdu_init(struct mrp_applicant *app)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct mrp_pdu_hdr *ph;
|
||||
|
||||
skb = alloc_skb(app->dev->mtu + LL_RESERVED_SPACE(app->dev),
|
||||
GFP_ATOMIC);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
skb->dev = app->dev;
|
||||
skb->protocol = app->app->pkttype.type;
|
||||
skb_reserve(skb, LL_RESERVED_SPACE(app->dev));
|
||||
skb_reset_network_header(skb);
|
||||
skb_reset_transport_header(skb);
|
||||
|
||||
ph = (struct mrp_pdu_hdr *)__skb_put(skb, sizeof(*ph));
|
||||
ph->version = app->app->version;
|
||||
|
||||
app->pdu = skb;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mrp_pdu_append_end_mark(struct mrp_applicant *app)
|
||||
{
|
||||
__be16 *endmark;
|
||||
|
||||
if (skb_tailroom(app->pdu) < sizeof(*endmark))
|
||||
return -1;
|
||||
endmark = (__be16 *)__skb_put(app->pdu, sizeof(*endmark));
|
||||
put_unaligned(MRP_END_MARK, endmark);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mrp_pdu_queue(struct mrp_applicant *app)
|
||||
{
|
||||
if (!app->pdu)
|
||||
return;
|
||||
|
||||
if (mrp_cb(app->pdu)->mh)
|
||||
mrp_pdu_append_end_mark(app);
|
||||
mrp_pdu_append_end_mark(app);
|
||||
|
||||
dev_hard_header(app->pdu, app->dev, ntohs(app->app->pkttype.type),
|
||||
app->app->group_address, app->dev->dev_addr,
|
||||
app->pdu->len);
|
||||
|
||||
skb_queue_tail(&app->queue, app->pdu);
|
||||
app->pdu = NULL;
|
||||
}
|
||||
|
||||
static void mrp_queue_xmit(struct mrp_applicant *app)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
while ((skb = skb_dequeue(&app->queue)))
|
||||
dev_queue_xmit(skb);
|
||||
}
|
||||
|
||||
static int mrp_pdu_append_msg_hdr(struct mrp_applicant *app,
|
||||
u8 attrtype, u8 attrlen)
|
||||
{
|
||||
struct mrp_msg_hdr *mh;
|
||||
|
||||
if (mrp_cb(app->pdu)->mh) {
|
||||
if (mrp_pdu_append_end_mark(app) < 0)
|
||||
return -1;
|
||||
mrp_cb(app->pdu)->mh = NULL;
|
||||
mrp_cb(app->pdu)->vah = NULL;
|
||||
}
|
||||
|
||||
if (skb_tailroom(app->pdu) < sizeof(*mh))
|
||||
return -1;
|
||||
mh = (struct mrp_msg_hdr *)__skb_put(app->pdu, sizeof(*mh));
|
||||
mh->attrtype = attrtype;
|
||||
mh->attrlen = attrlen;
|
||||
mrp_cb(app->pdu)->mh = mh;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mrp_pdu_append_vecattr_hdr(struct mrp_applicant *app,
|
||||
const void *firstattrvalue, u8 attrlen)
|
||||
{
|
||||
struct mrp_vecattr_hdr *vah;
|
||||
|
||||
if (skb_tailroom(app->pdu) < sizeof(*vah) + attrlen)
|
||||
return -1;
|
||||
vah = (struct mrp_vecattr_hdr *)__skb_put(app->pdu,
|
||||
sizeof(*vah) + attrlen);
|
||||
put_unaligned(0, &vah->lenflags);
|
||||
memcpy(vah->firstattrvalue, firstattrvalue, attrlen);
|
||||
mrp_cb(app->pdu)->vah = vah;
|
||||
memcpy(mrp_cb(app->pdu)->attrvalue, firstattrvalue, attrlen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mrp_pdu_append_vecattr_event(struct mrp_applicant *app,
|
||||
const struct mrp_attr *attr,
|
||||
enum mrp_vecattr_event vaevent)
|
||||
{
|
||||
u16 len, pos;
|
||||
u8 *vaevents;
|
||||
int err;
|
||||
again:
|
||||
if (!app->pdu) {
|
||||
err = mrp_pdu_init(app);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* If there is no Message header in the PDU, or the Message header is
|
||||
* for a different attribute type, add an EndMark (if necessary) and a
|
||||
* new Message header to the PDU.
|
||||
*/
|
||||
if (!mrp_cb(app->pdu)->mh ||
|
||||
mrp_cb(app->pdu)->mh->attrtype != attr->type ||
|
||||
mrp_cb(app->pdu)->mh->attrlen != attr->len) {
|
||||
if (mrp_pdu_append_msg_hdr(app, attr->type, attr->len) < 0)
|
||||
goto queue;
|
||||
}
|
||||
|
||||
/* If there is no VectorAttribute header for this Message in the PDU,
|
||||
* or this attribute's value does not sequentially follow the previous
|
||||
* attribute's value, add a new VectorAttribute header to the PDU.
|
||||
*/
|
||||
if (!mrp_cb(app->pdu)->vah ||
|
||||
memcmp(mrp_cb(app->pdu)->attrvalue, attr->value, attr->len)) {
|
||||
if (mrp_pdu_append_vecattr_hdr(app, attr->value, attr->len) < 0)
|
||||
goto queue;
|
||||
}
|
||||
|
||||
len = be16_to_cpu(get_unaligned(&mrp_cb(app->pdu)->vah->lenflags));
|
||||
pos = len % 3;
|
||||
|
||||
/* Events are packed into Vectors in the PDU, three to a byte. Add a
|
||||
* byte to the end of the Vector if necessary.
|
||||
*/
|
||||
if (!pos) {
|
||||
if (skb_tailroom(app->pdu) < sizeof(u8))
|
||||
goto queue;
|
||||
vaevents = (u8 *)__skb_put(app->pdu, sizeof(u8));
|
||||
} else {
|
||||
vaevents = (u8 *)(skb_tail_pointer(app->pdu) - sizeof(u8));
|
||||
}
|
||||
|
||||
switch (pos) {
|
||||
case 0:
|
||||
*vaevents = vaevent * (__MRP_VECATTR_EVENT_MAX *
|
||||
__MRP_VECATTR_EVENT_MAX);
|
||||
break;
|
||||
case 1:
|
||||
*vaevents += vaevent * __MRP_VECATTR_EVENT_MAX;
|
||||
break;
|
||||
case 2:
|
||||
*vaevents += vaevent;
|
||||
break;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
}
|
||||
|
||||
/* Increment the length of the VectorAttribute in the PDU, as well as
|
||||
* the value of the next attribute that would continue its Vector.
|
||||
*/
|
||||
put_unaligned(cpu_to_be16(++len), &mrp_cb(app->pdu)->vah->lenflags);
|
||||
mrp_attrvalue_inc(mrp_cb(app->pdu)->attrvalue, attr->len);
|
||||
|
||||
return 0;
|
||||
|
||||
queue:
|
||||
mrp_pdu_queue(app);
|
||||
goto again;
|
||||
}
|
||||
|
||||
static void mrp_attr_event(struct mrp_applicant *app,
|
||||
struct mrp_attr *attr, enum mrp_event event)
|
||||
{
|
||||
enum mrp_applicant_state state;
|
||||
|
||||
state = mrp_applicant_state_table[attr->state][event];
|
||||
if (state == MRP_APPLICANT_INVALID) {
|
||||
WARN_ON(1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (event == MRP_EVENT_TX) {
|
||||
/* When appending the attribute fails, don't update its state
|
||||
* in order to retry at the next TX event.
|
||||
*/
|
||||
|
||||
switch (mrp_tx_action_table[attr->state]) {
|
||||
case MRP_TX_ACTION_NONE:
|
||||
case MRP_TX_ACTION_S_JOIN_IN_OPTIONAL:
|
||||
case MRP_TX_ACTION_S_IN_OPTIONAL:
|
||||
break;
|
||||
case MRP_TX_ACTION_S_NEW:
|
||||
if (mrp_pdu_append_vecattr_event(
|
||||
app, attr, MRP_VECATTR_EVENT_NEW) < 0)
|
||||
return;
|
||||
break;
|
||||
case MRP_TX_ACTION_S_JOIN_IN:
|
||||
if (mrp_pdu_append_vecattr_event(
|
||||
app, attr, MRP_VECATTR_EVENT_JOIN_IN) < 0)
|
||||
return;
|
||||
break;
|
||||
case MRP_TX_ACTION_S_LV:
|
||||
if (mrp_pdu_append_vecattr_event(
|
||||
app, attr, MRP_VECATTR_EVENT_LV) < 0)
|
||||
return;
|
||||
/* As a pure applicant, sending a leave message
|
||||
* implies that the attribute was unregistered and
|
||||
* can be destroyed.
|
||||
*/
|
||||
mrp_attr_destroy(app, attr);
|
||||
return;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
}
|
||||
}
|
||||
|
||||
attr->state = state;
|
||||
}
|
||||
|
||||
int mrp_request_join(const struct net_device *dev,
|
||||
const struct mrp_application *appl,
|
||||
const void *value, u8 len, u8 type)
|
||||
{
|
||||
struct mrp_port *port = rtnl_dereference(dev->mrp_port);
|
||||
struct mrp_applicant *app = rtnl_dereference(
|
||||
port->applicants[appl->type]);
|
||||
struct mrp_attr *attr;
|
||||
|
||||
if (sizeof(struct mrp_skb_cb) + len >
|
||||
FIELD_SIZEOF(struct sk_buff, cb))
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_bh(&app->lock);
|
||||
attr = mrp_attr_create(app, value, len, type);
|
||||
if (!attr) {
|
||||
spin_unlock_bh(&app->lock);
|
||||
return -ENOMEM;
|
||||
}
|
||||
mrp_attr_event(app, attr, MRP_EVENT_JOIN);
|
||||
spin_unlock_bh(&app->lock);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mrp_request_join);
|
||||
|
||||
void mrp_request_leave(const struct net_device *dev,
|
||||
const struct mrp_application *appl,
|
||||
const void *value, u8 len, u8 type)
|
||||
{
|
||||
struct mrp_port *port = rtnl_dereference(dev->mrp_port);
|
||||
struct mrp_applicant *app = rtnl_dereference(
|
||||
port->applicants[appl->type]);
|
||||
struct mrp_attr *attr;
|
||||
|
||||
if (sizeof(struct mrp_skb_cb) + len >
|
||||
FIELD_SIZEOF(struct sk_buff, cb))
|
||||
return;
|
||||
|
||||
spin_lock_bh(&app->lock);
|
||||
attr = mrp_attr_lookup(app, value, len, type);
|
||||
if (!attr) {
|
||||
spin_unlock_bh(&app->lock);
|
||||
return;
|
||||
}
|
||||
mrp_attr_event(app, attr, MRP_EVENT_LV);
|
||||
spin_unlock_bh(&app->lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mrp_request_leave);
|
||||
|
||||
static void mrp_mad_event(struct mrp_applicant *app, enum mrp_event event)
|
||||
{
|
||||
struct rb_node *node, *next;
|
||||
struct mrp_attr *attr;
|
||||
|
||||
for (node = rb_first(&app->mad);
|
||||
next = node ? rb_next(node) : NULL, node != NULL;
|
||||
node = next) {
|
||||
attr = rb_entry(node, struct mrp_attr, node);
|
||||
mrp_attr_event(app, attr, event);
|
||||
}
|
||||
}
|
||||
|
||||
static void mrp_join_timer_arm(struct mrp_applicant *app)
|
||||
{
|
||||
unsigned long delay;
|
||||
|
||||
delay = (u64)msecs_to_jiffies(mrp_join_time) * prandom_u32() >> 32;
|
||||
mod_timer(&app->join_timer, jiffies + delay);
|
||||
}
|
||||
|
||||
static void mrp_join_timer(unsigned long data)
|
||||
{
|
||||
struct mrp_applicant *app = (struct mrp_applicant *)data;
|
||||
|
||||
spin_lock(&app->lock);
|
||||
mrp_mad_event(app, MRP_EVENT_TX);
|
||||
mrp_pdu_queue(app);
|
||||
spin_unlock(&app->lock);
|
||||
|
||||
mrp_queue_xmit(app);
|
||||
mrp_join_timer_arm(app);
|
||||
}
|
||||
|
||||
static void mrp_periodic_timer_arm(struct mrp_applicant *app)
|
||||
{
|
||||
mod_timer(&app->periodic_timer,
|
||||
jiffies + msecs_to_jiffies(mrp_periodic_time));
|
||||
}
|
||||
|
||||
static void mrp_periodic_timer(unsigned long data)
|
||||
{
|
||||
struct mrp_applicant *app = (struct mrp_applicant *)data;
|
||||
|
||||
spin_lock(&app->lock);
|
||||
mrp_mad_event(app, MRP_EVENT_PERIODIC);
|
||||
mrp_pdu_queue(app);
|
||||
spin_unlock(&app->lock);
|
||||
|
||||
mrp_periodic_timer_arm(app);
|
||||
}
|
||||
|
||||
static int mrp_pdu_parse_end_mark(struct sk_buff *skb, int *offset)
|
||||
{
|
||||
__be16 endmark;
|
||||
|
||||
if (skb_copy_bits(skb, *offset, &endmark, sizeof(endmark)) < 0)
|
||||
return -1;
|
||||
if (endmark == MRP_END_MARK) {
|
||||
*offset += sizeof(endmark);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mrp_pdu_parse_vecattr_event(struct mrp_applicant *app,
|
||||
struct sk_buff *skb,
|
||||
enum mrp_vecattr_event vaevent)
|
||||
{
|
||||
struct mrp_attr *attr;
|
||||
enum mrp_event event;
|
||||
|
||||
attr = mrp_attr_lookup(app, mrp_cb(skb)->attrvalue,
|
||||
mrp_cb(skb)->mh->attrlen,
|
||||
mrp_cb(skb)->mh->attrtype);
|
||||
if (attr == NULL)
|
||||
return;
|
||||
|
||||
switch (vaevent) {
|
||||
case MRP_VECATTR_EVENT_NEW:
|
||||
event = MRP_EVENT_R_NEW;
|
||||
break;
|
||||
case MRP_VECATTR_EVENT_JOIN_IN:
|
||||
event = MRP_EVENT_R_JOIN_IN;
|
||||
break;
|
||||
case MRP_VECATTR_EVENT_IN:
|
||||
event = MRP_EVENT_R_IN;
|
||||
break;
|
||||
case MRP_VECATTR_EVENT_JOIN_MT:
|
||||
event = MRP_EVENT_R_JOIN_MT;
|
||||
break;
|
||||
case MRP_VECATTR_EVENT_MT:
|
||||
event = MRP_EVENT_R_MT;
|
||||
break;
|
||||
case MRP_VECATTR_EVENT_LV:
|
||||
event = MRP_EVENT_R_LV;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
mrp_attr_event(app, attr, event);
|
||||
}
|
||||
|
||||
static int mrp_pdu_parse_vecattr(struct mrp_applicant *app,
|
||||
struct sk_buff *skb, int *offset)
|
||||
{
|
||||
struct mrp_vecattr_hdr _vah;
|
||||
u16 valen;
|
||||
u8 vaevents, vaevent;
|
||||
|
||||
mrp_cb(skb)->vah = skb_header_pointer(skb, *offset, sizeof(_vah),
|
||||
&_vah);
|
||||
if (!mrp_cb(skb)->vah)
|
||||
return -1;
|
||||
*offset += sizeof(_vah);
|
||||
|
||||
if (get_unaligned(&mrp_cb(skb)->vah->lenflags) &
|
||||
MRP_VECATTR_HDR_FLAG_LA)
|
||||
mrp_mad_event(app, MRP_EVENT_R_LA);
|
||||
valen = be16_to_cpu(get_unaligned(&mrp_cb(skb)->vah->lenflags) &
|
||||
MRP_VECATTR_HDR_LEN_MASK);
|
||||
|
||||
/* The VectorAttribute structure in a PDU carries event information
|
||||
* about one or more attributes having consecutive values. Only the
|
||||
* value for the first attribute is contained in the structure. So
|
||||
* we make a copy of that value, and then increment it each time we
|
||||
* advance to the next event in its Vector.
|
||||
*/
|
||||
if (sizeof(struct mrp_skb_cb) + mrp_cb(skb)->mh->attrlen >
|
||||
FIELD_SIZEOF(struct sk_buff, cb))
|
||||
return -1;
|
||||
if (skb_copy_bits(skb, *offset, mrp_cb(skb)->attrvalue,
|
||||
mrp_cb(skb)->mh->attrlen) < 0)
|
||||
return -1;
|
||||
*offset += mrp_cb(skb)->mh->attrlen;
|
||||
|
||||
/* In a VectorAttribute, the Vector contains events which are packed
|
||||
* three to a byte. We process one byte of the Vector at a time.
|
||||
*/
|
||||
while (valen > 0) {
|
||||
if (skb_copy_bits(skb, *offset, &vaevents,
|
||||
sizeof(vaevents)) < 0)
|
||||
return -1;
|
||||
*offset += sizeof(vaevents);
|
||||
|
||||
/* Extract and process the first event. */
|
||||
vaevent = vaevents / (__MRP_VECATTR_EVENT_MAX *
|
||||
__MRP_VECATTR_EVENT_MAX);
|
||||
if (vaevent >= __MRP_VECATTR_EVENT_MAX) {
|
||||
/* The byte is malformed; stop processing. */
|
||||
return -1;
|
||||
}
|
||||
mrp_pdu_parse_vecattr_event(app, skb, vaevent);
|
||||
|
||||
/* If present, extract and process the second event. */
|
||||
if (!--valen)
|
||||
break;
|
||||
mrp_attrvalue_inc(mrp_cb(skb)->attrvalue,
|
||||
mrp_cb(skb)->mh->attrlen);
|
||||
vaevents %= (__MRP_VECATTR_EVENT_MAX *
|
||||
__MRP_VECATTR_EVENT_MAX);
|
||||
vaevent = vaevents / __MRP_VECATTR_EVENT_MAX;
|
||||
mrp_pdu_parse_vecattr_event(app, skb, vaevent);
|
||||
|
||||
/* If present, extract and process the third event. */
|
||||
if (!--valen)
|
||||
break;
|
||||
mrp_attrvalue_inc(mrp_cb(skb)->attrvalue,
|
||||
mrp_cb(skb)->mh->attrlen);
|
||||
vaevents %= __MRP_VECATTR_EVENT_MAX;
|
||||
vaevent = vaevents;
|
||||
mrp_pdu_parse_vecattr_event(app, skb, vaevent);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mrp_pdu_parse_msg(struct mrp_applicant *app, struct sk_buff *skb,
|
||||
int *offset)
|
||||
{
|
||||
struct mrp_msg_hdr _mh;
|
||||
|
||||
mrp_cb(skb)->mh = skb_header_pointer(skb, *offset, sizeof(_mh), &_mh);
|
||||
if (!mrp_cb(skb)->mh)
|
||||
return -1;
|
||||
*offset += sizeof(_mh);
|
||||
|
||||
if (mrp_cb(skb)->mh->attrtype == 0 ||
|
||||
mrp_cb(skb)->mh->attrtype > app->app->maxattr ||
|
||||
mrp_cb(skb)->mh->attrlen == 0)
|
||||
return -1;
|
||||
|
||||
while (skb->len > *offset) {
|
||||
if (mrp_pdu_parse_end_mark(skb, offset) < 0)
|
||||
break;
|
||||
if (mrp_pdu_parse_vecattr(app, skb, offset) < 0)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mrp_rcv(struct sk_buff *skb, struct net_device *dev,
|
||||
struct packet_type *pt, struct net_device *orig_dev)
|
||||
{
|
||||
struct mrp_application *appl = container_of(pt, struct mrp_application,
|
||||
pkttype);
|
||||
struct mrp_port *port;
|
||||
struct mrp_applicant *app;
|
||||
struct mrp_pdu_hdr _ph;
|
||||
const struct mrp_pdu_hdr *ph;
|
||||
int offset = skb_network_offset(skb);
|
||||
|
||||
/* If the interface is in promiscuous mode, drop the packet if
|
||||
* it was unicast to another host.
|
||||
*/
|
||||
if (unlikely(skb->pkt_type == PACKET_OTHERHOST))
|
||||
goto out;
|
||||
skb = skb_share_check(skb, GFP_ATOMIC);
|
||||
if (unlikely(!skb))
|
||||
goto out;
|
||||
port = rcu_dereference(dev->mrp_port);
|
||||
if (unlikely(!port))
|
||||
goto out;
|
||||
app = rcu_dereference(port->applicants[appl->type]);
|
||||
if (unlikely(!app))
|
||||
goto out;
|
||||
|
||||
ph = skb_header_pointer(skb, offset, sizeof(_ph), &_ph);
|
||||
if (!ph)
|
||||
goto out;
|
||||
offset += sizeof(_ph);
|
||||
|
||||
if (ph->version != app->app->version)
|
||||
goto out;
|
||||
|
||||
spin_lock(&app->lock);
|
||||
while (skb->len > offset) {
|
||||
if (mrp_pdu_parse_end_mark(skb, &offset) < 0)
|
||||
break;
|
||||
if (mrp_pdu_parse_msg(app, skb, &offset) < 0)
|
||||
break;
|
||||
}
|
||||
spin_unlock(&app->lock);
|
||||
out:
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mrp_init_port(struct net_device *dev)
|
||||
{
|
||||
struct mrp_port *port;
|
||||
|
||||
port = kzalloc(sizeof(*port), GFP_KERNEL);
|
||||
if (!port)
|
||||
return -ENOMEM;
|
||||
rcu_assign_pointer(dev->mrp_port, port);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mrp_release_port(struct net_device *dev)
|
||||
{
|
||||
struct mrp_port *port = rtnl_dereference(dev->mrp_port);
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i <= MRP_APPLICATION_MAX; i++) {
|
||||
if (rtnl_dereference(port->applicants[i]))
|
||||
return;
|
||||
}
|
||||
RCU_INIT_POINTER(dev->mrp_port, NULL);
|
||||
kfree_rcu(port, rcu);
|
||||
}
|
||||
|
||||
int mrp_init_applicant(struct net_device *dev, struct mrp_application *appl)
|
||||
{
|
||||
struct mrp_applicant *app;
|
||||
int err;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
if (!rtnl_dereference(dev->mrp_port)) {
|
||||
err = mrp_init_port(dev);
|
||||
if (err < 0)
|
||||
goto err1;
|
||||
}
|
||||
|
||||
err = -ENOMEM;
|
||||
app = kzalloc(sizeof(*app), GFP_KERNEL);
|
||||
if (!app)
|
||||
goto err2;
|
||||
|
||||
err = dev_mc_add(dev, appl->group_address);
|
||||
if (err < 0)
|
||||
goto err3;
|
||||
|
||||
app->dev = dev;
|
||||
app->app = appl;
|
||||
app->mad = RB_ROOT;
|
||||
spin_lock_init(&app->lock);
|
||||
skb_queue_head_init(&app->queue);
|
||||
rcu_assign_pointer(dev->mrp_port->applicants[appl->type], app);
|
||||
setup_timer(&app->join_timer, mrp_join_timer, (unsigned long)app);
|
||||
mrp_join_timer_arm(app);
|
||||
setup_timer(&app->periodic_timer, mrp_periodic_timer,
|
||||
(unsigned long)app);
|
||||
mrp_periodic_timer_arm(app);
|
||||
return 0;
|
||||
|
||||
err3:
|
||||
kfree(app);
|
||||
err2:
|
||||
mrp_release_port(dev);
|
||||
err1:
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mrp_init_applicant);
|
||||
|
||||
void mrp_uninit_applicant(struct net_device *dev, struct mrp_application *appl)
|
||||
{
|
||||
struct mrp_port *port = rtnl_dereference(dev->mrp_port);
|
||||
struct mrp_applicant *app = rtnl_dereference(
|
||||
port->applicants[appl->type]);
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
RCU_INIT_POINTER(port->applicants[appl->type], NULL);
|
||||
|
||||
/* Delete timer and generate a final TX event to flush out
|
||||
* all pending messages before the applicant is gone.
|
||||
*/
|
||||
del_timer_sync(&app->join_timer);
|
||||
del_timer_sync(&app->periodic_timer);
|
||||
|
||||
spin_lock_bh(&app->lock);
|
||||
mrp_mad_event(app, MRP_EVENT_TX);
|
||||
mrp_pdu_queue(app);
|
||||
spin_unlock_bh(&app->lock);
|
||||
|
||||
mrp_queue_xmit(app);
|
||||
|
||||
dev_mc_del(dev, appl->group_address);
|
||||
kfree_rcu(app, rcu);
|
||||
mrp_release_port(dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mrp_uninit_applicant);
|
||||
|
||||
int mrp_register_application(struct mrp_application *appl)
|
||||
{
|
||||
appl->pkttype.func = mrp_rcv;
|
||||
dev_add_pack(&appl->pkttype);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mrp_register_application);
|
||||
|
||||
void mrp_unregister_application(struct mrp_application *appl)
|
||||
{
|
||||
dev_remove_pack(&appl->pkttype);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mrp_unregister_application);
|
66
net/802/p8022.c
Normal file
66
net/802/p8022.c
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* NET3: Support for 802.2 demultiplexing off Ethernet
|
||||
* 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.
|
||||
*
|
||||
* Demultiplex 802.2 encoded protocols. We match the entry by the
|
||||
* SSAP/DSAP pair and then deliver to the registered datalink that
|
||||
* matches. The control byte is ignored and handling of such items
|
||||
* is up to the routine passed the frame.
|
||||
*
|
||||
* Unlike the 802.3 datalink we have a list of 802.2 entries as
|
||||
* there are multiple protocols to demux. The list is currently
|
||||
* short (3 or 4 entries at most). The current demux assumes this.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/slab.h>
|
||||
#include <net/datalink.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/init.h>
|
||||
#include <net/llc.h>
|
||||
#include <net/p8022.h>
|
||||
|
||||
static int p8022_request(struct datalink_proto *dl, struct sk_buff *skb,
|
||||
unsigned char *dest)
|
||||
{
|
||||
llc_build_and_send_ui_pkt(dl->sap, skb, dest, dl->sap->laddr.lsap);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct datalink_proto *register_8022_client(unsigned char type,
|
||||
int (*func)(struct sk_buff *skb,
|
||||
struct net_device *dev,
|
||||
struct packet_type *pt,
|
||||
struct net_device *orig_dev))
|
||||
{
|
||||
struct datalink_proto *proto;
|
||||
|
||||
proto = kmalloc(sizeof(*proto), GFP_ATOMIC);
|
||||
if (proto) {
|
||||
proto->type[0] = type;
|
||||
proto->header_length = 3;
|
||||
proto->request = p8022_request;
|
||||
proto->sap = llc_sap_open(type, func);
|
||||
if (!proto->sap) {
|
||||
kfree(proto);
|
||||
proto = NULL;
|
||||
}
|
||||
}
|
||||
return proto;
|
||||
}
|
||||
|
||||
void unregister_8022_client(struct datalink_proto *proto)
|
||||
{
|
||||
llc_sap_put(proto->sap);
|
||||
kfree(proto);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(register_8022_client);
|
||||
EXPORT_SYMBOL(unregister_8022_client);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
64
net/802/p8023.c
Normal file
64
net/802/p8023.c
Normal file
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* NET3: 802.3 data link hooks used for IPX 802.3
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 802.3 isn't really a protocol data link layer. Some old IPX stuff
|
||||
* uses it however. Note that there is only one 802.3 protocol layer
|
||||
* in the system. We don't currently support different protocols
|
||||
* running raw 802.3 on different devices. Thankfully nobody else
|
||||
* has done anything like the old IPX.
|
||||
*/
|
||||
|
||||
#include <linux/in.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <net/datalink.h>
|
||||
#include <net/p8022.h>
|
||||
|
||||
/*
|
||||
* Place an 802.3 header on a packet. The driver will do the mac
|
||||
* addresses, we just need to give it the buffer length.
|
||||
*/
|
||||
static int p8023_request(struct datalink_proto *dl,
|
||||
struct sk_buff *skb, unsigned char *dest_node)
|
||||
{
|
||||
struct net_device *dev = skb->dev;
|
||||
|
||||
dev_hard_header(skb, dev, ETH_P_802_3, dest_node, NULL, skb->len);
|
||||
return dev_queue_xmit(skb);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create an 802.3 client. Note there can be only one 802.3 client
|
||||
*/
|
||||
struct datalink_proto *make_8023_client(void)
|
||||
{
|
||||
struct datalink_proto *proto = kmalloc(sizeof(*proto), GFP_ATOMIC);
|
||||
|
||||
if (proto) {
|
||||
proto->header_length = 0;
|
||||
proto->request = p8023_request;
|
||||
}
|
||||
return proto;
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy the 802.3 client.
|
||||
*/
|
||||
void destroy_8023_client(struct datalink_proto *dl)
|
||||
{
|
||||
kfree(dl);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(destroy_8023_client);
|
||||
EXPORT_SYMBOL(make_8023_client);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
167
net/802/psnap.c
Normal file
167
net/802/psnap.c
Normal file
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
* SNAP data link layer. Derived from 802.2
|
||||
*
|
||||
* Alan Cox <alan@lxorguk.ukuu.org.uk>,
|
||||
* from the 802.2 layer by Greg Page.
|
||||
* Merged in additions from Greg Page's psnap.c.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/slab.h>
|
||||
#include <net/datalink.h>
|
||||
#include <net/llc.h>
|
||||
#include <net/psnap.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/rculist.h>
|
||||
|
||||
static LIST_HEAD(snap_list);
|
||||
static DEFINE_SPINLOCK(snap_lock);
|
||||
static struct llc_sap *snap_sap;
|
||||
|
||||
/*
|
||||
* Find a snap client by matching the 5 bytes.
|
||||
*/
|
||||
static struct datalink_proto *find_snap_client(const unsigned char *desc)
|
||||
{
|
||||
struct datalink_proto *proto = NULL, *p;
|
||||
|
||||
list_for_each_entry_rcu(p, &snap_list, node) {
|
||||
if (!memcmp(p->type, desc, 5)) {
|
||||
proto = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return proto;
|
||||
}
|
||||
|
||||
/*
|
||||
* A SNAP packet has arrived
|
||||
*/
|
||||
static int snap_rcv(struct sk_buff *skb, struct net_device *dev,
|
||||
struct packet_type *pt, struct net_device *orig_dev)
|
||||
{
|
||||
int rc = 1;
|
||||
struct datalink_proto *proto;
|
||||
static struct packet_type snap_packet_type = {
|
||||
.type = cpu_to_be16(ETH_P_SNAP),
|
||||
};
|
||||
|
||||
if (unlikely(!pskb_may_pull(skb, 5)))
|
||||
goto drop;
|
||||
|
||||
rcu_read_lock();
|
||||
proto = find_snap_client(skb_transport_header(skb));
|
||||
if (proto) {
|
||||
/* Pass the frame on. */
|
||||
skb->transport_header += 5;
|
||||
skb_pull_rcsum(skb, 5);
|
||||
rc = proto->rcvfunc(skb, dev, &snap_packet_type, orig_dev);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
if (unlikely(!proto))
|
||||
goto drop;
|
||||
|
||||
out:
|
||||
return rc;
|
||||
|
||||
drop:
|
||||
kfree_skb(skb);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Put a SNAP header on a frame and pass to 802.2
|
||||
*/
|
||||
static int snap_request(struct datalink_proto *dl,
|
||||
struct sk_buff *skb, u8 *dest)
|
||||
{
|
||||
memcpy(skb_push(skb, 5), dl->type, 5);
|
||||
llc_build_and_send_ui_pkt(snap_sap, skb, dest, snap_sap->laddr.lsap);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up the SNAP layer
|
||||
*/
|
||||
EXPORT_SYMBOL(register_snap_client);
|
||||
EXPORT_SYMBOL(unregister_snap_client);
|
||||
|
||||
static const char snap_err_msg[] __initconst =
|
||||
KERN_CRIT "SNAP - unable to register with 802.2\n";
|
||||
|
||||
static int __init snap_init(void)
|
||||
{
|
||||
snap_sap = llc_sap_open(0xAA, snap_rcv);
|
||||
if (!snap_sap) {
|
||||
printk(snap_err_msg);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_init(snap_init);
|
||||
|
||||
static void __exit snap_exit(void)
|
||||
{
|
||||
llc_sap_put(snap_sap);
|
||||
}
|
||||
|
||||
module_exit(snap_exit);
|
||||
|
||||
|
||||
/*
|
||||
* Register SNAP clients. We don't yet use this for IP.
|
||||
*/
|
||||
struct datalink_proto *register_snap_client(const unsigned char *desc,
|
||||
int (*rcvfunc)(struct sk_buff *,
|
||||
struct net_device *,
|
||||
struct packet_type *,
|
||||
struct net_device *))
|
||||
{
|
||||
struct datalink_proto *proto = NULL;
|
||||
|
||||
spin_lock_bh(&snap_lock);
|
||||
|
||||
if (find_snap_client(desc))
|
||||
goto out;
|
||||
|
||||
proto = kmalloc(sizeof(*proto), GFP_ATOMIC);
|
||||
if (proto) {
|
||||
memcpy(proto->type, desc, 5);
|
||||
proto->rcvfunc = rcvfunc;
|
||||
proto->header_length = 5 + 3; /* snap + 802.2 */
|
||||
proto->request = snap_request;
|
||||
list_add_rcu(&proto->node, &snap_list);
|
||||
}
|
||||
out:
|
||||
spin_unlock_bh(&snap_lock);
|
||||
|
||||
return proto;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unregister SNAP clients. Protocols no longer want to play with us ...
|
||||
*/
|
||||
void unregister_snap_client(struct datalink_proto *proto)
|
||||
{
|
||||
spin_lock_bh(&snap_lock);
|
||||
list_del_rcu(&proto->node);
|
||||
spin_unlock_bh(&snap_lock);
|
||||
|
||||
synchronize_net();
|
||||
|
||||
kfree(proto);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
104
net/802/stp.c
Normal file
104
net/802/stp.c
Normal file
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* STP SAP demux
|
||||
*
|
||||
* Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/llc.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <net/llc.h>
|
||||
#include <net/llc_pdu.h>
|
||||
#include <net/stp.h>
|
||||
|
||||
/* 01:80:c2:00:00:20 - 01:80:c2:00:00:2F */
|
||||
#define GARP_ADDR_MIN 0x20
|
||||
#define GARP_ADDR_MAX 0x2F
|
||||
#define GARP_ADDR_RANGE (GARP_ADDR_MAX - GARP_ADDR_MIN)
|
||||
|
||||
static const struct stp_proto __rcu *garp_protos[GARP_ADDR_RANGE + 1] __read_mostly;
|
||||
static const struct stp_proto __rcu *stp_proto __read_mostly;
|
||||
|
||||
static struct llc_sap *sap __read_mostly;
|
||||
static unsigned int sap_registered;
|
||||
static DEFINE_MUTEX(stp_proto_mutex);
|
||||
|
||||
/* Called under rcu_read_lock from LLC */
|
||||
static int stp_pdu_rcv(struct sk_buff *skb, struct net_device *dev,
|
||||
struct packet_type *pt, struct net_device *orig_dev)
|
||||
{
|
||||
const struct ethhdr *eh = eth_hdr(skb);
|
||||
const struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
|
||||
const struct stp_proto *proto;
|
||||
|
||||
if (pdu->ssap != LLC_SAP_BSPAN ||
|
||||
pdu->dsap != LLC_SAP_BSPAN ||
|
||||
pdu->ctrl_1 != LLC_PDU_TYPE_U)
|
||||
goto err;
|
||||
|
||||
if (eh->h_dest[5] >= GARP_ADDR_MIN && eh->h_dest[5] <= GARP_ADDR_MAX) {
|
||||
proto = rcu_dereference(garp_protos[eh->h_dest[5] -
|
||||
GARP_ADDR_MIN]);
|
||||
if (proto &&
|
||||
!ether_addr_equal(eh->h_dest, proto->group_address))
|
||||
goto err;
|
||||
} else
|
||||
proto = rcu_dereference(stp_proto);
|
||||
|
||||
if (!proto)
|
||||
goto err;
|
||||
|
||||
proto->rcv(proto, skb, dev);
|
||||
return 0;
|
||||
|
||||
err:
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int stp_proto_register(const struct stp_proto *proto)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
mutex_lock(&stp_proto_mutex);
|
||||
if (sap_registered++ == 0) {
|
||||
sap = llc_sap_open(LLC_SAP_BSPAN, stp_pdu_rcv);
|
||||
if (!sap) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if (is_zero_ether_addr(proto->group_address))
|
||||
rcu_assign_pointer(stp_proto, proto);
|
||||
else
|
||||
rcu_assign_pointer(garp_protos[proto->group_address[5] -
|
||||
GARP_ADDR_MIN], proto);
|
||||
out:
|
||||
mutex_unlock(&stp_proto_mutex);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(stp_proto_register);
|
||||
|
||||
void stp_proto_unregister(const struct stp_proto *proto)
|
||||
{
|
||||
mutex_lock(&stp_proto_mutex);
|
||||
if (is_zero_ether_addr(proto->group_address))
|
||||
RCU_INIT_POINTER(stp_proto, NULL);
|
||||
else
|
||||
RCU_INIT_POINTER(garp_protos[proto->group_address[5] -
|
||||
GARP_ADDR_MIN], NULL);
|
||||
synchronize_rcu();
|
||||
|
||||
if (--sap_registered == 0)
|
||||
llc_sap_put(sap);
|
||||
mutex_unlock(&stp_proto_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(stp_proto_unregister);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
Loading…
Add table
Add a link
Reference in a new issue