| --[[ | |
| Copyright 2016 Marek Vavrusa <mvavrusa@cloudflare.com> | |
| Licensed under the Apache License, Version 2.0 (the "License"); | |
| you may not use this file except in compliance with the License. | |
| You may obtain a copy of the License at | |
| http://www.apache.org/licenses/LICENSE-2.0 | |
| Unless required by applicable law or agreed to in writing, software | |
| distributed under the License is distributed on an "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| See the License for the specific language governing permissions and | |
| limitations under the License. | |
| ]] | |
| local ffi = require('ffi') | |
| local BPF = ffi.typeof('struct bpf') | |
| ffi.cdef [[ | |
| struct sk_buff { | |
| uint32_t len; | |
| uint32_t pkt_type; | |
| uint32_t mark; | |
| uint32_t queue_mapping; | |
| uint32_t protocol; | |
| uint32_t vlan_present; | |
| uint32_t vlan_tci; | |
| uint32_t vlan_proto; | |
| uint32_t priority; | |
| uint32_t ingress_ifindex; | |
| uint32_t ifindex; | |
| uint32_t tc_index; | |
| uint32_t cb[5]; | |
| uint32_t hash; | |
| uint32_t tc_classid; | |
| uint32_t data; | |
| uint32_t data_end; | |
| uint32_t napi_id; | |
| /* Accessed by BPF_PROG_TYPE_sk_skb types from here to ... */ | |
| uint32_t family; | |
| uint32_t remote_ip4; /* Stored in network byte order */ | |
| uint32_t local_ip4; /* Stored in network byte order */ | |
| uint32_t remote_ip6[4]; /* Stored in network byte order */ | |
| uint32_t local_ip6[4]; /* Stored in network byte order */ | |
| uint32_t remote_port; /* Stored in network byte order */ | |
| uint32_t local_port; /* stored in host byte order */ | |
| /* ... here. */ | |
| uint32_t data_meta; | |
| }; | |
| struct net_off_t { | |
| uint8_t ver:4; | |
| } __attribute__((packed)); | |
| struct eth_t { | |
| uint8_t dst[6]; | |
| uint8_t src[6]; | |
| uint16_t type; | |
| } __attribute__((packed)); | |
| struct dot1q_t { | |
| uint16_t pri:3; | |
| uint16_t cfi:1; | |
| uint16_t vlanid:12; | |
| uint16_t type; | |
| } __attribute__((packed)); | |
| struct arp_t { | |
| uint16_t htype; | |
| uint16_t ptype; | |
| uint8_t hlen; | |
| uint8_t plen; | |
| uint16_t oper; | |
| uint8_t sha[6]; | |
| uint32_t spa; | |
| uint8_t tha[6]; | |
| uint32_t tpa; | |
| } __attribute__((packed)); | |
| struct ip_t { | |
| uint8_t ver:4; | |
| uint8_t hlen:4; | |
| uint8_t tos; | |
| uint16_t tlen; | |
| uint16_t identification; | |
| uint16_t ffo_unused:1; | |
| uint16_t df:1; | |
| uint16_t mf:1; | |
| uint16_t foffset:13; | |
| uint8_t ttl; | |
| uint8_t proto; | |
| uint16_t hchecksum; | |
| uint32_t src; | |
| uint32_t dst; | |
| } __attribute__((packed)); | |
| struct icmp_t { | |
| uint8_t type; | |
| uint8_t code; | |
| uint16_t checksum; | |
| } __attribute__((packed)); | |
| struct ip6_t { | |
| uint32_t ver:4; | |
| uint32_t priority:8; | |
| uint32_t flow_label:20; | |
| uint16_t payload_len; | |
| uint8_t next_header; | |
| uint8_t hop_limit; | |
| uint64_t src_hi; | |
| uint64_t src_lo; | |
| uint64_t dst_hi; | |
| uint64_t dst_lo; | |
| } __attribute__((packed)); | |
| struct ip6_opt_t { | |
| uint8_t next_header; | |
| uint8_t ext_len; | |
| uint8_t pad[6]; | |
| } __attribute__((packed)); | |
| struct icmp6_t { | |
| uint8_t type; | |
| uint8_t code; | |
| uint16_t checksum; | |
| } __attribute__((packed)); | |
| struct udp_t { | |
| uint16_t src_port; | |
| uint16_t dst_port; | |
| uint16_t length; | |
| uint16_t crc; | |
| } __attribute__((packed)); | |
| struct tcp_t { | |
| uint16_t src_port; | |
| uint16_t dst_port; | |
| uint32_t seq_num; | |
| uint32_t ack_num; | |
| uint8_t offset:4; | |
| uint8_t reserved:4; | |
| uint8_t flag_cwr:1; | |
| uint8_t flag_ece:1; | |
| uint8_t flag_urg:1; | |
| uint8_t flag_ack:1; | |
| uint8_t flag_psh:1; | |
| uint8_t flag_rst:1; | |
| uint8_t flag_syn:1; | |
| uint8_t flag_fin:1; | |
| uint16_t rcv_wnd; | |
| uint16_t cksum; | |
| uint16_t urg_ptr; | |
| } __attribute__((packed)); | |
| struct vxlan_t { | |
| uint32_t rsv1:4; | |
| uint32_t iflag:1; | |
| uint32_t rsv2:3; | |
| uint32_t rsv3:24; | |
| uint32_t key:24; | |
| uint32_t rsv4:8; | |
| } __attribute__((packed)); | |
| ]] | |
| -- Architecture-specific ptrace register layout | |
| local S = require('syscall') | |
| local arch = S.abi.arch | |
| local parm_to_reg = {} | |
| if arch == 'x64' then | |
| ffi.cdef [[ | |
| struct pt_regs { | |
| unsigned long r15; | |
| unsigned long r14; | |
| unsigned long r13; | |
| unsigned long r12; | |
| unsigned long bp; | |
| unsigned long bx; | |
| unsigned long r11; | |
| unsigned long r10; | |
| unsigned long r9; | |
| unsigned long r8; | |
| unsigned long ax; | |
| unsigned long cx; | |
| unsigned long dx; | |
| unsigned long si; | |
| unsigned long di; | |
| unsigned long orig_ax; | |
| unsigned long ip; | |
| unsigned long cs; | |
| unsigned long flags; | |
| unsigned long sp; | |
| unsigned long ss; | |
| };]] | |
| parm_to_reg = {parm1='di', parm2='si', parm3='dx', parm4='cx', parm5='r8', ret='sp', fp='bp'} | |
| else | |
| ffi.cdef 'struct pt_regs {};' | |
| end | |
| -- Map symbolic registers to architecture ABI | |
| ffi.metatype('struct pt_regs', { | |
| __index = function (_ --[[t]],k) | |
| return assert(parm_to_reg[k], 'no such register: '..k) | |
| end, | |
| }) | |
| local M = {} | |
| -- Dissector interface | |
| local function dissector(type, e, dst, src, field) | |
| local parent = e.V[src].const | |
| -- Create new dissector variable | |
| e.vcopy(dst, src) | |
| -- Compute and materialize new dissector offset from parent | |
| e.V[dst].const = {off=e.V[src].const.off, __dissector=e.V[src].const.__dissector} | |
| parent.__dissector[field](e, dst) | |
| e.V[dst].const.__dissector = type | |
| end | |
| M.dissector = dissector | |
| -- Get current effective offset, load field value at an offset relative to it and | |
| -- add its value to compute next effective offset (e.g. udp_off = ip_off + pkt[ip_off].hlen) | |
| local function next_offset(e, var, type, off, mask, shift) | |
| local d = e.V[var].const | |
| -- Materialize relative offset value in R0 | |
| local dst_reg, tmp_reg | |
| if d.off then | |
| dst_reg = e.vreg(var, 0, true) | |
| tmp_reg = dst_reg -- Use target register to avoid copy | |
| e.emit(BPF.LD + BPF.ABS + e.const_width[ffi.sizeof(type)], tmp_reg, 0, 0, d.off + off or 0) | |
| else | |
| tmp_reg = e.vreg(e.tmpvar, 0, true, type) -- Reserve R0 for temporary relative offset | |
| dst_reg = e.vreg(var) -- Must rematerialize (if it was spilled by tmp var) | |
| e.emit(BPF.LD + BPF.IND + e.const_width[ffi.sizeof(type)], tmp_reg, dst_reg, 0, off or 0) | |
| end | |
| -- Finalize relative offset | |
| if mask then | |
| e.emit(BPF.ALU + BPF.AND + BPF.K, tmp_reg, 0, 0, mask) | |
| end | |
| if shift and shift ~= 0 then | |
| local op = BPF.LSH | |
| if shift < 0 then | |
| op = BPF.RSH | |
| shift = -shift | |
| end | |
| e.emit(BPF.ALU + op + BPF.K, tmp_reg, 0, 0, shift) | |
| end | |
| -- Add to base offset to turn it into effective address | |
| if dst_reg ~= tmp_reg then | |
| e.emit(BPF.ALU + BPF.ADD + BPF.X, dst_reg, tmp_reg, 0, 0) | |
| else | |
| e.emit(BPF.ALU + BPF.ADD + BPF.K, dst_reg, 0, 0, d.off) | |
| end | |
| -- Discard temporary allocations | |
| d.off = nil | |
| e.V[e.tmpvar].reg = nil | |
| end | |
| local function next_skip(e, var, off) | |
| local d = e.V[var].const | |
| if not d.off then | |
| local dst_reg = e.vreg(var) | |
| e.emit(BPF.ALU64 + BPF.ADD + BPF.K, dst_reg, 0, 0, off) | |
| else | |
| d.off = d.off + off | |
| end | |
| end | |
| local function skip_eth(e, dst) | |
| -- IP starts right after ETH header (fixed size) | |
| local d = e.V[dst].const | |
| d.off = d.off + ffi.sizeof('struct eth_t') | |
| end | |
| -- Export types | |
| M.type = function(typestr, t) | |
| t = t or {} | |
| t.__dissector=ffi.typeof(typestr) | |
| return t | |
| end | |
| M.skb = M.type('struct sk_buff', {source='ptr_to_ctx'}) | |
| M.pt_regs = M.type('struct pt_regs', {source='ptr_to_probe'}) | |
| M.pkt = M.type('struct eth_t', {off=0, source='ptr_to_pkt'}) -- skb needs special accessors | |
| -- M.eth = function (...) return dissector(ffi.typeof('struct eth_t'), ...) end | |
| M.dot1q = function (...) return dissector(ffi.typeof('struct dot1q_t'), ...) end | |
| M.arp = function (...) return dissector(ffi.typeof('struct arp_t'), ...) end | |
| M.icmp = function (...) return dissector(ffi.typeof('struct icmp_t'), ...) end | |
| M.ip = function (...) return dissector(ffi.typeof('struct ip_t'), ...) end | |
| M.icmp6 = function (...) return dissector(ffi.typeof('struct icmp6_t'), ...) end | |
| M.ip6 = function (...) return dissector(ffi.typeof('struct ip6_t'), ...) end | |
| M.ip6_opt = function (...) return dissector(ffi.typeof('struct ip6_opt_t'), ...) end | |
| M.udp = function (...) return dissector(ffi.typeof('struct udp_t'), ...) end | |
| M.tcp = function (...) return dissector(ffi.typeof('struct tcp_t'), ...) end | |
| M.vxlan = function (...) return dissector(ffi.typeof('struct vxlan_t'), ...) end | |
| M.data = function (...) return dissector(ffi.typeof('uint8_t'), ...) end | |
| M.net_off = function (...) return dissector(ffi.typeof('struct net_off_t'), ...) end | |
| -- Metatables | |
| ffi.metatype(ffi.typeof('struct eth_t'), { | |
| __index = { | |
| ip = skip_eth, | |
| ip6 = skip_eth, | |
| net_off = function (e, dst) | |
| next_skip(e, dst, BPF.NET_OFF) | |
| end, | |
| } | |
| }) | |
| ffi.metatype(ffi.typeof('struct net_off_t'), { | |
| __index = { | |
| ip = function () end, | |
| ip6 = function () end, | |
| } | |
| }) | |
| ffi.metatype(ffi.typeof('struct ip_t'), { | |
| __index = { | |
| -- Skip IP header length (stored as number of words) | |
| -- e.g. hlen = 5, Header Length = 5 x sizeof(u32) = 20 octets | |
| -- Mask first nibble and shift by 2 (multiplication by 4) | |
| icmp = function(e, dst) next_offset(e, dst, ffi.typeof('uint8_t'), 0, 0x0f, 2) end, | |
| udp = function(e, dst) next_offset(e, dst, ffi.typeof('uint8_t'), 0, 0x0f, 2) end, | |
| tcp = function(e, dst) next_offset(e, dst, ffi.typeof('uint8_t'), 0, 0x0f, 2) end, | |
| } | |
| }) | |
| ffi.metatype(ffi.typeof('struct ip6_t'), { | |
| __index = { | |
| -- Skip fixed IPv6 header length (40 bytes) | |
| -- The caller must check the value of `next_header` to skip any extension headers | |
| icmp6 = function(e, dst) next_skip(e, dst, ffi.sizeof('struct ip6_t'), 0) end, | |
| udp = function(e, dst) next_skip(e, dst, ffi.sizeof('struct ip6_t'), 0) end, | |
| tcp = function(e, dst) next_skip(e, dst, ffi.sizeof('struct ip6_t'), 0) end, | |
| ip6_opt = function(e, dst) next_skip(e, dst, ffi.sizeof('struct ip6_t'), 0) end, | |
| } | |
| }) | |
| local ip6_opt_ext_len_off = ffi.offsetof('struct ip6_opt_t', 'ext_len') | |
| ffi.metatype(ffi.typeof('struct ip6_opt_t'), { | |
| __index = { | |
| -- Skip IPv6 extension header length (field `ext_len`) | |
| icmp6 = function(e, dst) next_offset(e, dst, ffi.typeof('uint8_t'), ip6_opt_ext_len_off) end, | |
| udp = function(e, dst) next_offset(e, dst, ffi.typeof('uint8_t'), ip6_opt_ext_len_off) end, | |
| tcp = function(e, dst) next_offset(e, dst, ffi.typeof('uint8_t'), ip6_opt_ext_len_off) end, | |
| ip6_opt = function(e, dst) next_offset(e, dst, ffi.typeof('uint8_t'), ip6_opt_ext_len_off) end, | |
| } | |
| }) | |
| ffi.metatype(ffi.typeof('struct tcp_t'), { | |
| __index = { | |
| -- Skip TCP header length (stored as number of words) | |
| -- e.g. hlen = 5, Header Length = 5 x sizeof(u32) = 20 octets | |
| data = function(e, dst) | |
| next_offset(e, dst, ffi.typeof('uint8_t'), ffi.offsetof('struct tcp_t', 'offset'), 0xf0, -2) | |
| end, | |
| } | |
| }) | |
| ffi.metatype(ffi.typeof('struct udp_t'), { | |
| __index = { | |
| -- Skip UDP header length (8 octets) | |
| data = function(e, dst) | |
| next_skip(e, dst, ffi.sizeof('struct udp_t')) | |
| end, | |
| } | |
| }) | |
| -- Constants | |
| M.c = { | |
| eth = { -- Constants http://standards.ieee.org/regauth/ethertype | |
| ip = 0x0800, -- IP (v4) protocol | |
| ip6 = 0x86dd, -- IP (v6) protocol | |
| arp = 0x0806, -- Address resolution protocol | |
| revarp = 0x8035, -- Reverse addr resolution protocol | |
| vlan = 0x8100, -- IEEE 802.1Q VLAN tagging | |
| }, | |
| ip = { | |
| -- Reserved Addresses | |
| addr_any = 0x00000000, -- 0.0.0.0 | |
| addr_broadcast = 0xffffffff, -- 255.255.255.255 | |
| addr_loopback = 0x7f000001, -- 127.0.0.1 | |
| addr_mcast_all = 0xe0000001, -- 224.0.0.1 | |
| addr_mcast_local = 0xe00000ff, -- 224.0.0.255 | |
| -- Type of service (ip_tos), RFC 1349 ("obsoleted by RFC 2474") | |
| tos_default = 0x00, -- default | |
| tos_lowdelay = 0x10, -- low delay | |
| tos_throughput = 0x08, -- high throughput | |
| tos_reliability = 0x04, -- high reliability | |
| tos_lowcost = 0x02, -- low monetary cost - XXX | |
| tos_ect = 0x02, -- ECN-capable transport | |
| tos_ce = 0x01, -- congestion experienced | |
| -- Fragmentation flags (ip_off) | |
| rf = 0x8000, -- reserved | |
| df = 0x4000, -- don't fragment | |
| mf = 0x2000, -- more fragments (not last frag) | |
| offmask = 0x1fff, -- mask for fragment offset | |
| -- Time-to-live (ip_ttl), seconds | |
| ttl_default = 64, -- default ttl, RFC 1122, RFC 1340 | |
| ttl_max = 255, -- maximum ttl | |
| -- Protocol (ip_p) - http://www.iana.org/assignments/protocol-numbers | |
| proto_ip = 0, -- dummy for IP | |
| proto_hopopts = 0, -- IPv6 hop-by-hop options | |
| proto_icmp = 1, -- ICMP | |
| proto_igmp = 2, -- IGMP | |
| proto_ggp = 3, -- gateway-gateway protocol | |
| proto_ipip = 4, -- IP in IP | |
| proto_st = 5, -- ST datagram mode | |
| proto_tcp = 6, -- TCP | |
| proto_cbt = 7, -- CBT | |
| proto_egp = 8, -- exterior gateway protocol | |
| proto_igp = 9, -- interior gateway protocol | |
| proto_bbnrcc = 10, -- BBN RCC monitoring | |
| proto_nvp = 11, -- Network Voice Protocol | |
| proto_pup = 12, -- PARC universal packet | |
| proto_argus = 13, -- ARGUS | |
| proto_emcon = 14, -- EMCON | |
| proto_xnet = 15, -- Cross Net Debugger | |
| proto_chaos = 16, -- Chaos | |
| proto_udp = 17, -- UDP | |
| proto_mux = 18, -- multiplexing | |
| proto_dcnmeas = 19, -- DCN measurement | |
| proto_hmp = 20, -- Host Monitoring Protocol | |
| proto_prm = 21, -- Packet Radio Measurement | |
| proto_idp = 22, -- Xerox NS IDP | |
| proto_trunk1 = 23, -- Trunk-1 | |
| proto_trunk2 = 24, -- Trunk-2 | |
| proto_leaf1 = 25, -- Leaf-1 | |
| proto_leaf2 = 26, -- Leaf-2 | |
| proto_rdp = 27, -- "Reliable Datagram" proto | |
| proto_irtp = 28, -- Inet Reliable Transaction | |
| proto_tp = 29, -- ISO TP class 4 | |
| proto_netblt = 30, -- Bulk Data Transfer | |
| proto_mfpnsp = 31, -- MFE Network Services | |
| proto_meritinp= 32, -- Merit Internodal Protocol | |
| proto_sep = 33, -- Sequential Exchange proto | |
| proto_3pc = 34, -- Third Party Connect proto | |
| proto_idpr = 35, -- Interdomain Policy Route | |
| proto_xtp = 36, -- Xpress Transfer Protocol | |
| proto_ddp = 37, -- Datagram Delivery Proto | |
| proto_cmtp = 38, -- IDPR Ctrl Message Trans | |
| proto_tppp = 39, -- TP++ Transport Protocol | |
| proto_il = 40, -- IL Transport Protocol | |
| proto_ip6 = 41, -- IPv6 | |
| proto_sdrp = 42, -- Source Demand Routing | |
| proto_routing = 43, -- IPv6 routing header | |
| proto_fragment= 44, -- IPv6 fragmentation header | |
| proto_rsvp = 46, -- Reservation protocol | |
| proto_gre = 47, -- General Routing Encap | |
| proto_mhrp = 48, -- Mobile Host Routing | |
| proto_ena = 49, -- ENA | |
| proto_esp = 50, -- Encap Security Payload | |
| proto_ah = 51, -- Authentication Header | |
| proto_inlsp = 52, -- Integated Net Layer Sec | |
| proto_swipe = 53, -- SWIPE | |
| proto_narp = 54, -- NBMA Address Resolution | |
| proto_mobile = 55, -- Mobile IP, RFC 2004 | |
| proto_tlsp = 56, -- Transport Layer Security | |
| proto_skip = 57, -- SKIP | |
| proto_icmp6 = 58, -- ICMP for IPv6 | |
| proto_none = 59, -- IPv6 no next header | |
| proto_dstopts = 60, -- IPv6 destination options | |
| proto_anyhost = 61, -- any host internal proto | |
| proto_cftp = 62, -- CFTP | |
| proto_anynet = 63, -- any local network | |
| proto_expak = 64, -- SATNET and Backroom EXPAK | |
| proto_kryptolan = 65, -- Kryptolan | |
| proto_rvd = 66, -- MIT Remote Virtual Disk | |
| proto_ippc = 67, -- Inet Pluribus Packet Core | |
| proto_distfs = 68, -- any distributed fs | |
| proto_satmon = 69, -- SATNET Monitoring | |
| proto_visa = 70, -- VISA Protocol | |
| proto_ipcv = 71, -- Inet Packet Core Utility | |
| proto_cpnx = 72, -- Comp Proto Net Executive | |
| proto_cphb = 73, -- Comp Protocol Heart Beat | |
| proto_wsn = 74, -- Wang Span Network | |
| proto_pvp = 75, -- Packet Video Protocol | |
| proto_brsatmon= 76, -- Backroom SATNET Monitor | |
| proto_sunnd = 77, -- SUN ND Protocol | |
| proto_wbmon = 78, -- WIDEBAND Monitoring | |
| proto_wbexpak = 79, -- WIDEBAND EXPAK | |
| proto_eon = 80, -- ISO CNLP | |
| proto_vmtp = 81, -- Versatile Msg Transport | |
| proto_svmtp = 82, -- Secure VMTP | |
| proto_vines = 83, -- VINES | |
| proto_ttp = 84, -- TTP | |
| proto_nsfigp = 85, -- NSFNET-IGP | |
| proto_dgp = 86, -- Dissimilar Gateway Proto | |
| proto_tcf = 87, -- TCF | |
| proto_eigrp = 88, -- EIGRP | |
| proto_ospf = 89, -- Open Shortest Path First | |
| proto_spriterpc= 90, -- Sprite RPC Protocol | |
| proto_larp = 91, -- Locus Address Resolution | |
| proto_mtp = 92, -- Multicast Transport Proto | |
| proto_ax25 = 93, -- AX.25 Frames | |
| proto_ipipencap= 94, -- yet-another IP encap | |
| proto_micp = 95, -- Mobile Internet Ctrl | |
| proto_sccsp = 96, -- Semaphore Comm Sec Proto | |
| proto_etherip = 97, -- Ethernet in IPv4 | |
| proto_encap = 98, -- encapsulation header | |
| proto_anyenc = 99, -- private encryption scheme | |
| proto_gmtp = 100, -- GMTP | |
| proto_ifmp = 101, -- Ipsilon Flow Mgmt Proto | |
| proto_pnni = 102, -- PNNI over IP | |
| proto_pim = 103, -- Protocol Indep Multicast | |
| proto_aris = 104, -- ARIS | |
| proto_scps = 105, -- SCPS | |
| proto_qnx = 106, -- QNX | |
| proto_an = 107, -- Active Networks | |
| proto_ipcomp = 108, -- IP Payload Compression | |
| proto_snp = 109, -- Sitara Networks Protocol | |
| proto_compaqpeer= 110, -- Compaq Peer Protocol | |
| proto_ipxip = 111, -- IPX in IP | |
| proto_vrrp = 112, -- Virtual Router Redundancy | |
| proto_pgm = 113, -- PGM Reliable Transport | |
| proto_any0hop = 114, -- 0-hop protocol | |
| proto_l2tp = 115, -- Layer 2 Tunneling Proto | |
| proto_ddx = 116, -- D-II Data Exchange (DDX) | |
| proto_iatp = 117, -- Interactive Agent Xfer | |
| proto_stp = 118, -- Schedule Transfer Proto | |
| proto_srp = 119, -- SpectraLink Radio Proto | |
| proto_uti = 120, -- UTI | |
| proto_smp = 121, -- Simple Message Protocol | |
| proto_sm = 122, -- SM | |
| proto_ptp = 123, -- Performance Transparency | |
| proto_isis = 124, -- ISIS over IPv4 | |
| proto_fire = 125, -- FIRE | |
| proto_crtp = 126, -- Combat Radio Transport | |
| proto_crudp = 127, -- Combat Radio UDP | |
| proto_sscopmce= 128, -- SSCOPMCE | |
| proto_iplt = 129, -- IPLT | |
| proto_sps = 130, -- Secure Packet Shield | |
| proto_pipe = 131, -- Private IP Encap in IP | |
| proto_sctp = 132, -- Stream Ctrl Transmission | |
| proto_fc = 133, -- Fibre Channel | |
| proto_rsvpign = 134, -- RSVP-E2E-IGNORE | |
| proto_raw = 255, -- Raw IP packets | |
| proto_reserved= 255, -- Reserved | |
| }, | |
| } | |
| return M |