1/* 2 * Copyright (c) 2012, 2015 The Linux Foundation. All rights reserved. 3 * Permission to use, copy, modify, and/or distribute this software for any 4 * purpose with or without fee is hereby granted, provided that the above 5 * copyright notice and this permission notice appear in all copies. 6 * 7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 */ 15 16#include <linux/kernel.h> 17#include <linux/netfilter.h> 18#include <linux/netdevice.h> 19#include <linux/etherdevice.h> 20#include <net/ip.h> 21#include <net/inet_ecn.h> 22#include <linux/netfilter_bridge.h> 23 24#include "mc_snooping.h" 25#include "mc_private.h" 26#include "mc_api.h" 27#include "mc_osdep.h" 28 29 30static struct net_bridge_port *mc_br_port_get(int ifindex) 31{ 32 struct net_device *dev = NULL; 33 struct net_bridge_port *bp = NULL; 34 35 dev = dev_get_by_index(&init_net, ifindex); 36 37 if (dev) { 38 bp = os_br_port_get(dev); 39 dev_put(dev); 40 } 41 return bp; 42} 43 44#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0)) 45static unsigned int mc_pre_routing_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, 46 const struct net_device *in, const struct net_device *out, 47 int(*okfn)(struct sk_buff *)) 48#else 49static unsigned int mc_pre_routing_hook(unsigned int hooknum, struct sk_buff *skb, 50 const struct net_device *in, const struct net_device *out, 51 int(*okfn)(struct sk_buff *)) 52#endif 53{ 54 struct mc_struct *mc = MC_DEV(in); 55 struct ethhdr *eh = eth_hdr(skb); 56 struct net_bridge_port *port; 57 u8 dscp; 58 59 if (!mc || skb->pkt_type != PACKET_HOST || 60 (port = os_br_port_get(in)) == NULL) 61 goto out; 62 63 dscp = MC_DSCP(mc->dscp) & (~INET_ECN_MASK); 64 65 switch (ntohs(skb->protocol)) { 66 case ETH_P_IP: 67 { 68 const struct iphdr *iph = ip_hdr(skb); 69 if (ipv4_is_multicast(iph->daddr) && (!mc->enable_retag || 70 (mc->enable_retag && (ipv4_get_dsfield(iph) == dscp)))) { 71 ip_eth_mc_map(iph->daddr, eh->h_dest); 72 73 if (mc->debug && printk_ratelimit()) { 74 MC_PRINT("Decap the group "MC_IP4_STR" back to "MC_MAC_STR"\n", 75 MC_IP4_FMT((u8 *)(&iph->daddr)), MC_MAC_FMT(eh->h_dest)); 76 } 77 skb->pkt_type = PACKET_MULTICAST; 78 } 79 } 80 break; 81#ifdef MC_SUPPORT_MLD 82 case ETH_P_IPV6: 83 { 84 struct ipv6hdr *iph6 = ipv6_hdr(skb); 85 if (ipv6_addr_is_multicast(&iph6->daddr) && (!mc->enable_retag || 86 (mc->enable_retag && (ipv6_get_dsfield(iph6) == dscp)))) { 87 ipv6_eth_mc_map(&iph6->daddr, eh->h_dest); 88 89 if (mc->debug && printk_ratelimit()) { 90 MC_PRINT("Decap the group "MC_IP6_STR" back to "MC_MAC_STR"\n", 91 MC_IP6_FMT((__be16 *)(&iph6->daddr)), MC_MAC_FMT(eh->h_dest)); 92 } 93 skb->pkt_type = PACKET_MULTICAST; 94 } 95 } 96 break; 97#endif 98 } 99out: 100 return NF_ACCEPT; 101} 102 103#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0)) 104static unsigned int mc_forward_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, 105 const struct net_device *in, const struct net_device *out, 106 int(*okfn)(struct sk_buff *)) 107#else 108static unsigned int mc_forward_hook(unsigned int hooknum, struct sk_buff *skb, 109 const struct net_device *in, const struct net_device *out, 110 int(*okfn)(struct sk_buff *)) 111#endif 112{ 113 struct mc_struct *mc = MC_DEV(out); 114 struct hlist_head *rhead = NULL; 115 struct net_bridge_port *port; 116 117 if (MC_SKB_CB(skb)->type != MC_LEAVE && 118 MC_SKB_CB(skb)->type != MC_REPORT) 119 goto accept; 120 121 if ((port = os_br_port_get(in)) == NULL || !mc) 122 goto accept; 123 124 /* Report/Leave forward */ 125 if (mc->rp.type == MC_RTPORT_DEFAULT) { 126 if (ntohs(skb->protocol) == ETH_P_IP) 127 rhead = &mc->rp.igmp_rlist; 128#ifdef MC_SUPPORT_MLD 129 else 130 rhead = &mc->rp.mld_rlist; 131#endif 132 if (!hlist_empty(rhead)) { 133 struct mc_querier_entry *qe; 134 struct hlist_node *h; 135 136 os_hlist_for_each_entry_rcu(qe, h, rhead, rlist) { 137 if (((struct net_bridge_port *)qe->port)->dev == out) 138 goto accept; 139 } 140 goto drop; 141 } 142 } else if (mc->rp.type == MC_RTPORT_SPECIFY) { 143 port = mc_br_port_get(mc->rp.ifindex); 144 if (!port || port->dev != out) 145 goto drop; 146 } else { 147 goto drop; 148 } 149accept: 150 return NF_ACCEPT; 151drop: 152 return NF_DROP; 153} 154 155static struct nf_hook_ops mc_hook_ops[] __read_mostly = { 156 { 157 .pf = NFPROTO_BRIDGE, 158 .priority = 1, 159 .hooknum = NF_BR_PRE_ROUTING, 160 .hook = mc_pre_routing_hook, 161 .owner = THIS_MODULE, 162 }, 163 { 164 .pf = NFPROTO_BRIDGE, 165 .priority = 1, 166 .hooknum = NF_BR_FORWARD, 167 .hook = mc_forward_hook, 168 .owner = THIS_MODULE, 169 } 170}; 171 172 173int __init mc_netfilter_init(void) 174{ 175 int ret = 0; 176 ret = nf_register_hook(&mc_hook_ops[0]); 177 ret |= nf_register_hook(&mc_hook_ops[1]); 178 return ret; 179} 180 181void mc_netfilter_exit(void) 182{ 183 nf_unregister_hook(&mc_hook_ops[0]); 184 nf_unregister_hook(&mc_hook_ops[1]); 185} 186 187