linux/net/bridge/netfilter/ebtable_broute.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 *  ebtable_broute
   4 *
   5 *      Authors:
   6 *      Bart De Schuymer <bdschuym@pandora.be>
   7 *
   8 *  April, 2002
   9 *
  10 *  This table lets you choose between routing and bridging for frames
  11 *  entering on a bridge enslaved nic. This table is traversed before any
  12 *  other ebtables table. See net/bridge/br_input.c.
  13 */
  14
  15#include <linux/netfilter_bridge/ebtables.h>
  16#include <linux/module.h>
  17#include <linux/if_bridge.h>
  18
  19#include "../br_private.h"
  20
  21/* EBT_ACCEPT means the frame will be bridged
  22 * EBT_DROP means the frame will be routed
  23 */
  24static struct ebt_entries initial_chain = {
  25        .name           = "BROUTING",
  26        .policy         = EBT_ACCEPT,
  27};
  28
  29static struct ebt_replace_kernel initial_table = {
  30        .name           = "broute",
  31        .valid_hooks    = 1 << NF_BR_BROUTING,
  32        .entries_size   = sizeof(struct ebt_entries),
  33        .hook_entry     = {
  34                [NF_BR_BROUTING]        = &initial_chain,
  35        },
  36        .entries        = (char *)&initial_chain,
  37};
  38
  39static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
  40{
  41        if (valid_hooks & ~(1 << NF_BR_BROUTING))
  42                return -EINVAL;
  43        return 0;
  44}
  45
  46static const struct ebt_table broute_table = {
  47        .name           = "broute",
  48        .table          = &initial_table,
  49        .valid_hooks    = 1 << NF_BR_BROUTING,
  50        .check          = check,
  51        .me             = THIS_MODULE,
  52};
  53
  54static unsigned int ebt_broute(void *priv, struct sk_buff *skb,
  55                               const struct nf_hook_state *s)
  56{
  57        struct net_bridge_port *p = br_port_get_rcu(skb->dev);
  58        struct nf_hook_state state;
  59        unsigned char *dest;
  60        int ret;
  61
  62        if (!p || p->state != BR_STATE_FORWARDING)
  63                return NF_ACCEPT;
  64
  65        nf_hook_state_init(&state, NF_BR_BROUTING,
  66                           NFPROTO_BRIDGE, s->in, NULL, NULL,
  67                           s->net, NULL);
  68
  69        ret = ebt_do_table(skb, &state, priv);
  70        if (ret != NF_DROP)
  71                return ret;
  72
  73        /* DROP in ebtables -t broute means that the
  74         * skb should be routed, not bridged.
  75         * This is awkward, but can't be changed for compatibility
  76         * reasons.
  77         *
  78         * We map DROP to ACCEPT and set the ->br_netfilter_broute flag.
  79         */
  80        BR_INPUT_SKB_CB(skb)->br_netfilter_broute = 1;
  81
  82        /* undo PACKET_HOST mangling done in br_input in case the dst
  83         * address matches the logical bridge but not the port.
  84         */
  85        dest = eth_hdr(skb)->h_dest;
  86        if (skb->pkt_type == PACKET_HOST &&
  87            !ether_addr_equal(skb->dev->dev_addr, dest) &&
  88             ether_addr_equal(p->br->dev->dev_addr, dest))
  89                skb->pkt_type = PACKET_OTHERHOST;
  90
  91        return NF_ACCEPT;
  92}
  93
  94static const struct nf_hook_ops ebt_ops_broute = {
  95        .hook           = ebt_broute,
  96        .pf             = NFPROTO_BRIDGE,
  97        .hooknum        = NF_BR_PRE_ROUTING,
  98        .priority       = NF_BR_PRI_FIRST,
  99};
 100
 101static int broute_table_init(struct net *net)
 102{
 103        return ebt_register_table(net, &broute_table, &ebt_ops_broute);
 104}
 105
 106static void __net_exit broute_net_pre_exit(struct net *net)
 107{
 108        ebt_unregister_table_pre_exit(net, "broute");
 109}
 110
 111static void __net_exit broute_net_exit(struct net *net)
 112{
 113        ebt_unregister_table(net, "broute");
 114}
 115
 116static struct pernet_operations broute_net_ops = {
 117        .exit = broute_net_exit,
 118        .pre_exit = broute_net_pre_exit,
 119};
 120
 121static int __init ebtable_broute_init(void)
 122{
 123        int ret = ebt_register_template(&broute_table, broute_table_init);
 124
 125        if (ret)
 126                return ret;
 127
 128        ret = register_pernet_subsys(&broute_net_ops);
 129        if (ret) {
 130                ebt_unregister_template(&broute_table);
 131                return ret;
 132        }
 133
 134        return 0;
 135}
 136
 137static void __exit ebtable_broute_fini(void)
 138{
 139        unregister_pernet_subsys(&broute_net_ops);
 140        ebt_unregister_template(&broute_table);
 141}
 142
 143module_init(ebtable_broute_init);
 144module_exit(ebtable_broute_fini);
 145MODULE_LICENSE("GPL");
 146