📄 dvb_net.c
字号:
/* * dvb_net.c * * Copyright (C) 2001 Convergence integrated media GmbH * Ralph Metzler <ralph@convergence.de> * Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de> * * ULE Decapsulation code: * Copyright (C) 2003, 2004 gcs - Global Communication & Services GmbH. * and Department of Scientific Computing * Paris Lodron University of Salzburg. * Hilmar Linder <hlinder@cosy.sbg.ac.at> * and Wolfram Stering <wstering@cosy.sbg.ac.at> * * ULE Decaps according to draft-ietf-ipdvb-ule-03.txt. * * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * Or, point your browser to http://www.gnu.org/copyleft/gpl.html *//* * ULE ChangeLog: * Feb 2004: hl/ws v1: Implementing draft-fair-ipdvb-ule-01.txt * * Dec 2004: hl/ws v2: Implementing draft-ietf-ipdvb-ule-03.txt: * ULE Extension header handling. * Bugreports by Moritz Vieth and Hanno Tersteegen, * Fraunhofer Institute for Open Communication Systems * Competence Center for Advanced Satellite Communications. * Bugfixes and robustness improvements. * Filtering on dest MAC addresses, if present (D-Bit = 0) * ULE_DEBUG compile-time option. *//* * FIXME / TODO (dvb_net.c): * * Unloading does not work for 2.6.9 kernels: a refcount doesn't go to zero. * * TS_FEED callback is called once for every single TS cell although it is * registered (in dvb_net_feed_start()) for 100 TS cells (used for dvb_net_ule()). * */#include <linux/module.h>#include <linux/kernel.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/dvb/net.h>#include <linux/uio.h>#include <asm/uaccess.h>#include <linux/crc32.h>#include "dvb_demux.h"#include "dvb_net.h"static int dvb_net_debug;module_param(dvb_net_debug, int, 0444);MODULE_PARM_DESC(dvb_net_debug, "enable debug messages");#define dprintk(x...) do { if (dvb_net_debug) printk(x); } while (0)static inline __u32 iov_crc32( __u32 c, struct kvec *iov, unsigned int cnt ){ unsigned int j; for (j = 0; j < cnt; j++) c = crc32_be( c, iov[j].iov_base, iov[j].iov_len ); return c;}#define DVB_NET_MULTICAST_MAX 10#undef ULE_DEBUG#ifdef ULE_DEBUG#define isprint(c) ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'))static void hexdump( const unsigned char *buf, unsigned short len ){ char str[80], octet[10]; int ofs, i, l; for (ofs = 0; ofs < len; ofs += 16) { sprintf( str, "%03d: ", ofs ); for (i = 0; i < 16; i++) { if ((i + ofs) < len) sprintf( octet, "%02x ", buf[ofs + i] ); else strcpy( octet, " " ); strcat( str, octet ); } strcat( str, " " ); l = strlen( str ); for (i = 0; (i < 16) && ((i + ofs) < len); i++) str[l++] = isprint( buf[ofs + i] ) ? buf[ofs + i] : '.'; str[l] = '\0'; printk( KERN_WARNING "%s\n", str ); }}#endifstruct dvb_net_priv { int in_use; struct net_device_stats stats; u16 pid; struct dvb_net *host; struct dmx_demux *demux; struct dmx_section_feed *secfeed; struct dmx_section_filter *secfilter; struct dmx_ts_feed *tsfeed; int multi_num; struct dmx_section_filter *multi_secfilter[DVB_NET_MULTICAST_MAX]; unsigned char multi_macs[DVB_NET_MULTICAST_MAX][6]; int rx_mode;#define RX_MODE_UNI 0#define RX_MODE_MULTI 1#define RX_MODE_ALL_MULTI 2#define RX_MODE_PROMISC 3 struct work_struct set_multicast_list_wq; struct work_struct restart_net_feed_wq; unsigned char feedtype; /* Either FEED_TYPE_ or FEED_TYPE_ULE */ int need_pusi; /* Set to 1, if synchronization on PUSI required. */ unsigned char tscc; /* TS continuity counter after sync on PUSI. */ struct sk_buff *ule_skb; /* ULE SNDU decodes into this buffer. */ unsigned char *ule_next_hdr; /* Pointer into skb to next ULE extension header. */ unsigned short ule_sndu_len; /* ULE SNDU length in bytes, w/o D-Bit. */ unsigned short ule_sndu_type; /* ULE SNDU type field, complete. */ unsigned char ule_sndu_type_1; /* ULE SNDU type field, if split across 2 TS cells. */ unsigned char ule_dbit; /* Whether the DestMAC address present * or not (bit is set). */ unsigned char ule_bridged; /* Whether the ULE_BRIDGED extension header was found. */ int ule_sndu_remain; /* Nr. of bytes still required for current ULE SNDU. */ unsigned long ts_count; /* Current ts cell counter. */ struct semaphore mutex;};/** * Determine the packet's protocol ID. The rule here is that we * assume 802.3 if the type field is short enough to be a length. * This is normal practice and works for any 'now in use' protocol. * * stolen from eth.c out of the linux kernel, hacked for dvb-device * by Michael Holzt <kju@debian.org> */static unsigned short dvb_net_eth_type_trans(struct sk_buff *skb, struct net_device *dev){ struct ethhdr *eth; unsigned char *rawp; skb->mac.raw=skb->data; skb_pull(skb,dev->hard_header_len); eth = eth_hdr(skb); if (*eth->h_dest & 1) { if(memcmp(eth->h_dest,dev->broadcast, ETH_ALEN)==0) skb->pkt_type=PACKET_BROADCAST; else skb->pkt_type=PACKET_MULTICAST; } if (ntohs(eth->h_proto) >= 1536) return eth->h_proto; rawp = skb->data; /** * This is a magic hack to spot IPX packets. Older Novell breaks * the protocol design and runs IPX over 802.3 without an 802.2 LLC * layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This * won't work for fault tolerant netware but does for the rest. */ if (*(unsigned short *)rawp == 0xFFFF) return htons(ETH_P_802_3); /** * Real 802.2 LLC */ return htons(ETH_P_802_2);}#define TS_SZ 188#define TS_SYNC 0x47#define TS_TEI 0x80#define TS_SC 0xC0#define TS_PUSI 0x40#define TS_AF_A 0x20#define TS_AF_D 0x10/* ULE Extension Header handlers. */#define ULE_TEST 0#define ULE_BRIDGED 1static int ule_test_sndu( struct dvb_net_priv *p ){ return -1;}static int ule_bridged_sndu( struct dvb_net_priv *p ){ /* BRIDGE SNDU handling sucks in draft-ietf-ipdvb-ule-03.txt. * This has to be the last extension header, otherwise it won't work. * Blame the authors! */ p->ule_bridged = 1; return 0;}/** Handle ULE extension headers. * Function is called after a successful CRC32 verification of an ULE SNDU to complete its decoding. * Returns: >= 0: nr. of bytes consumed by next extension header * -1: Mandatory extension header that is not recognized or TEST SNDU; discard. */static int handle_one_ule_extension( struct dvb_net_priv *p ){ /* Table of mandatory extension header handlers. The header type is the index. */ static int (*ule_mandatory_ext_handlers[255])( struct dvb_net_priv *p ) = { [0] = ule_test_sndu, [1] = ule_bridged_sndu, [2] = NULL, }; /* Table of optional extension header handlers. The header type is the index. */ static int (*ule_optional_ext_handlers[255])( struct dvb_net_priv *p ) = { NULL, }; int ext_len = 0; unsigned char hlen = (p->ule_sndu_type & 0x0700) >> 8; unsigned char htype = p->ule_sndu_type & 0x00FF; /* Discriminate mandatory and optional extension headers. */ if (hlen == 0) { /* Mandatory extension header */ if (ule_mandatory_ext_handlers[htype]) { ext_len = ule_mandatory_ext_handlers[htype]( p ); p->ule_next_hdr += ext_len; if (! p->ule_bridged) { p->ule_sndu_type = ntohs( *(unsigned short *)p->ule_next_hdr ); p->ule_next_hdr += 2; } else { p->ule_sndu_type = ntohs( *(unsigned short *)(p->ule_next_hdr + ((p->ule_dbit ? 2 : 3) * ETH_ALEN)) ); /* This assures the extension handling loop will terminate. */ } } else ext_len = -1; /* SNDU has to be discarded. */ } else { /* Optional extension header. Calculate the length. */ ext_len = hlen << 2; /* Process the optional extension header according to its type. */ if (ule_optional_ext_handlers[htype]) (void)ule_optional_ext_handlers[htype]( p ); p->ule_next_hdr += ext_len; p->ule_sndu_type = ntohs( *(unsigned short *)p->ule_next_hdr ); p->ule_next_hdr += 2; } return ext_len;}static int handle_ule_extensions( struct dvb_net_priv *p ){ int total_ext_len = 0, l; p->ule_next_hdr = p->ule_skb->data; do { l = handle_one_ule_extension( p ); if (l == -1) return -1; /* Stop extension header processing and discard SNDU. */ total_ext_len += l; } while (p->ule_sndu_type < 1536); return total_ext_len;}/** Prepare for a new ULE SNDU: reset the decoder state. */static inline void reset_ule( struct dvb_net_priv *p ){ p->ule_skb = NULL; p->ule_next_hdr = NULL; p->ule_sndu_len = 0; p->ule_sndu_type = 0; p->ule_sndu_type_1 = 0; p->ule_sndu_remain = 0; p->ule_dbit = 0xFF; p->ule_bridged = 0;}/** * Decode ULE SNDUs according to draft-ietf-ipdvb-ule-03.txt from a sequence of * TS cells of a single PID. */static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ){ struct dvb_net_priv *priv = dev->priv; unsigned long skipped = 0L; u8 *ts, *ts_end, *from_where = NULL, ts_remain = 0, how_much = 0, new_ts = 1; struct ethhdr *ethh = NULL;#ifdef ULE_DEBUG /* The code inside ULE_DEBUG keeps a history of the last 100 TS cells processed. */ static unsigned char ule_hist[100*TS_SZ]; static unsigned char *ule_where = ule_hist, ule_dump = 0;#endif if (dev == NULL) { printk( KERN_ERR "NO netdev struct!\n" ); return; } /* For all TS cells in current buffer. * Appearently, we are called for every single TS cell. */ for (ts = (char *)buf, ts_end = (char *)buf + buf_len; ts < ts_end; /* no default incr. */ ) { if (new_ts) { /* We are about to process a new TS cell. */#ifdef ULE_DEBUG if (ule_where >= &ule_hist[100*TS_SZ]) ule_where = ule_hist; memcpy( ule_where, ts, TS_SZ ); if (ule_dump) { hexdump( ule_where, TS_SZ ); ule_dump = 0; } ule_where += TS_SZ;#endif /* Check TS error conditions: sync_byte, transport_error_indicator, scrambling_control . */ if ((ts[0] != TS_SYNC) || (ts[1] & TS_TEI) || ((ts[3] & TS_SC) != 0)) { printk(KERN_WARNING "%lu: Invalid TS cell: SYNC %#x, TEI %u, SC %#x.\n", priv->ts_count, ts[0], ts[1] & TS_TEI >> 7, ts[3] & 0xC0 >> 6); /* Drop partly decoded SNDU, reset state, resync on PUSI. */ if (priv->ule_skb) { dev_kfree_skb( priv->ule_skb ); /* Prepare for next SNDU. */ ((struct dvb_net_priv *) dev->priv)->stats.rx_errors++; ((struct dvb_net_priv *) dev->priv)->stats.rx_frame_errors++; } reset_ule(priv); priv->need_pusi = 1; /* Continue with next TS cell. */ ts += TS_SZ; priv->ts_count++; continue; } ts_remain = 184; from_where = ts + 4; } /* Synchronize on PUSI, if required. */ if (priv->need_pusi) { if (ts[1] & TS_PUSI) { /* Find beginning of first ULE SNDU in current TS cell. */ /* Synchronize continuity counter. */ priv->tscc = ts[3] & 0x0F; /* There is a pointer field here. */ if (ts[4] > ts_remain) { printk(KERN_ERR "%lu: Invalid ULE packet " "(pointer field %d)\n", priv->ts_count, ts[4]); ts += TS_SZ; priv->ts_count++; continue; } /* Skip to destination of pointer field. */ from_where = &ts[5] + ts[4]; ts_remain -= 1 + ts[4]; skipped = 0; } else { skipped++; ts += TS_SZ; priv->ts_count++; continue; } } /* Check continuity counter. */ if (new_ts) { if ((ts[3] & 0x0F) == priv->tscc) priv->tscc = (priv->tscc + 1) & 0x0F; else { /* TS discontinuity handling: */ printk(KERN_WARNING "%lu: TS discontinuity: got %#x, " "exptected %#x.\n", priv->ts_count, ts[3] & 0x0F, priv->tscc); /* Drop partly decoded SNDU, reset state, resync on PUSI. */ if (priv->ule_skb) { dev_kfree_skb( priv->ule_skb ); /* Prepare for next SNDU. */ // reset_ule(priv); moved to below. ((struct dvb_net_priv *) dev->priv)->stats.rx_errors++; ((struct dvb_net_priv *) dev->priv)->stats.rx_frame_errors++; } reset_ule(priv); /* skip to next PUSI. */ priv->need_pusi = 1; ts += TS_SZ; priv->ts_count++; continue; } /* If we still have an incomplete payload, but PUSI is * set; some TS cells are missing. * This is only possible here, if we missed exactly 16 TS * cells (continuity counter wrap). */ if (ts[1] & TS_PUSI) { if (! priv->need_pusi) { if (*from_where > 181) { /* Pointer field is invalid. Drop this TS cell and any started ULE SNDU. */ printk(KERN_WARNING "%lu: Invalid pointer " "field: %u.\n", priv->ts_count, *from_where); /* Drop partly decoded SNDU, reset state, resync on PUSI. */ if (priv->ule_skb) { dev_kfree_skb( priv->ule_skb ); ((struct dvb_net_priv *) dev->priv)->stats.rx_errors++; ((struct dvb_net_priv *) dev->priv)->stats.rx_frame_errors++; } reset_ule(priv); priv->need_pusi = 1; ts += TS_SZ; priv->ts_count++; continue; } /* Skip pointer field (we're processing a * packed payload). */ from_where += 1; ts_remain -= 1; } else priv->need_pusi = 0; if (priv->ule_sndu_remain > 183) { /* Current SNDU lacks more data than there could be available in the * current TS cell. */ ((struct dvb_net_priv *) dev->priv)->stats.rx_errors++; ((struct dvb_net_priv *) dev->priv)->stats.rx_length_errors++; printk(KERN_WARNING "%lu: Expected %d more SNDU bytes, but " "got PUSI (pf %d, ts_remain %d). Flushing incomplete payload.\n", priv->ts_count, priv->ule_sndu_remain, ts[4], ts_remain); dev_kfree_skb(priv->ule_skb); /* Prepare for next SNDU. */ reset_ule(priv); /* Resync: go to where pointer field points to: start of next ULE SNDU. */ from_where += ts[4]; ts_remain -= ts[4]; } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -