--- a/include/linux/if_bridge.h +++ b/include/linux/if_bridge.h @@ -71,6 +71,9 @@ void brioctl_set(int (*hook)(struct net void __user *uarg)); int br_ioctl_call(struct net *net, struct net_bridge *br, unsigned int cmd, struct ifreq *ifr, void __user *uarg); +extern void br_dev_update_stats(struct net_device *dev, + struct rtnl_link_stats64 *nlstats); +extern bool br_is_hairpin_enabled(struct net_device *dev); #if IS_ENABLED(CONFIG_BRIDGE) && IS_ENABLED(CONFIG_BRIDGE_IGMP_SNOOPING) int br_multicast_list_adjacent(struct net_device *dev, @@ -213,4 +216,42 @@ static inline clock_t br_get_ageing_time } #endif +/* QCA NSS ECM support - Start */ +extern struct net_device *br_port_dev_get(struct net_device *dev, + unsigned char *addr, + struct sk_buff *skb, + unsigned int cookie); +extern void br_refresh_fdb_entry(struct net_device *dev, const char *addr); +extern void br_fdb_entry_refresh(struct net_device *dev, const char *addr, __u16 vid); +extern struct net_bridge_fdb_entry *br_fdb_has_entry(struct net_device *dev, + const char *addr, + __u16 vid); +extern void br_fdb_update_register_notify(struct notifier_block *nb); +extern void br_fdb_update_unregister_notify(struct notifier_block *nb); + +typedef struct net_bridge_port *br_port_dev_get_hook_t(struct net_device *dev, + struct sk_buff *skb, + unsigned char *addr, + unsigned int cookie); +extern br_port_dev_get_hook_t __rcu *br_port_dev_get_hook; + +#define BR_FDB_EVENT_ADD 0x01 +#define BR_FDB_EVENT_DEL 0x02 + +struct br_fdb_event { + struct net_device *dev; + unsigned char addr[6]; + unsigned char is_local; + struct net_bridge *br; + struct net_device *orig_dev; +}; +extern void br_fdb_register_notify(struct notifier_block *nb); +extern void br_fdb_unregister_notify(struct notifier_block *nb); + +typedef struct net_bridge_port *br_get_dst_hook_t( + const struct net_bridge_port *src, + struct sk_buff **skb); +extern br_get_dst_hook_t __rcu *br_get_dst_hook; +/* QCA NSS ECM support - End */ + #endif --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h @@ -143,7 +143,10 @@ extern struct net_device *__vlan_find_de extern int vlan_for_each(struct net_device *dev, int (*action)(struct net_device *dev, int vid, void *arg), void *arg); +extern void __vlan_dev_update_accel_stats(struct net_device *dev, + struct rtnl_link_stats64 *stats); /* QCA NSS ECM support */ extern struct net_device *vlan_dev_real_dev(const struct net_device *dev); +extern struct net_device *vlan_dev_next_dev(const struct net_device *dev); /* QCA NSS ECM support */ extern u16 vlan_dev_vlan_id(const struct net_device *dev); extern __be16 vlan_dev_vlan_proto(const struct net_device *dev); @@ -236,6 +239,12 @@ extern void vlan_vids_del_by_dev(struct extern bool vlan_uses_dev(const struct net_device *dev); #else +static inline void __vlan_dev_update_accel_stats(struct net_device *dev, + struct rtnl_link_stats64 *stats) +{ + +} /* QCA NSS ECM support */ + static inline struct net_device * __vlan_find_dev_deep_rcu(struct net_device *real_dev, __be16 vlan_proto, u16 vlan_id) --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2936,6 +2936,10 @@ enum netdev_cmd { NETDEV_OFFLOAD_XSTATS_REPORT_USED, NETDEV_OFFLOAD_XSTATS_REPORT_DELTA, NETDEV_XDP_FEAT_CHANGE, + /* QCA NSS ECM Support - Start */ + NETDEV_BR_JOIN, + NETDEV_BR_LEAVE, + /* QCA NSS ECM Support - End */ }; const char *netdev_cmd_to_name(enum netdev_cmd cmd); --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h @@ -207,6 +207,11 @@ void rt6_multipath_rebalance(struct fib6 void rt6_uncached_list_add(struct rt6_info *rt); void rt6_uncached_list_del(struct rt6_info *rt); +/* QCA NSS ECM support - Start */ +int rt6_register_notifier(struct notifier_block *nb); +int rt6_unregister_notifier(struct notifier_block *nb); +/* QCA NSS ECM support - End */ + static inline const struct rt6_info *skb_rt6_info(const struct sk_buff *skb) { const struct dst_entry *dst = skb_dst(skb); --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -249,6 +249,13 @@ static inline int neigh_parms_family(str return p->tbl->family; } +/* QCA NSS ECM support - Start */ +struct neigh_mac_update { + unsigned char old_mac[ALIGN(MAX_ADDR_LEN, sizeof(unsigned long))]; + unsigned char update_mac[ALIGN(MAX_ADDR_LEN, sizeof(unsigned long))]; +}; +/* QCA NSS ECM support - End */ + #define NEIGH_PRIV_ALIGN sizeof(long long) #define NEIGH_ENTRY_SIZE(size) ALIGN((size), NEIGH_PRIV_ALIGN) @@ -395,6 +402,11 @@ void __neigh_for_each_release(struct nei int (*cb)(struct neighbour *)); int neigh_xmit(int fam, struct net_device *, const void *, struct sk_buff *); +/* QCA NSS ECM support - Start */ +extern void neigh_mac_update_register_notify(struct notifier_block *nb); +extern void neigh_mac_update_unregister_notify(struct notifier_block *nb); +/* QCA NSS ECM support - End */ + struct neigh_seq_state { struct seq_net_private p; struct neigh_table *tbl; @@ -600,4 +612,5 @@ static inline void neigh_update_is_route *notify = 1; } } + #endif --- a/include/net/route.h +++ b/include/net/route.h @@ -237,6 +237,11 @@ struct rtable *rt_dst_alloc(struct net_d unsigned int flags, u16 type, bool noxfrm); struct rtable *rt_dst_clone(struct net_device *dev, struct rtable *rt); +/* QCA NSS ECM support - Start */ +int ip_rt_register_notifier(struct notifier_block *nb); +int ip_rt_unregister_notifier(struct notifier_block *nb); +/* QCA NSS ECM support - End */ + struct in_ifaddr; void fib_add_ifaddr(struct in_ifaddr *); void fib_del_ifaddr(struct in_ifaddr *, struct in_ifaddr *); --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -2266,4 +2266,6 @@ void br_do_suppress_nd(struct sk_buff *s u16 vid, struct net_bridge_port *p, struct nd_msg *msg); struct nd_msg *br_is_nd_neigh_msg(struct sk_buff *skb, struct nd_msg *m); bool br_is_neigh_suppress_enabled(const struct net_bridge_port *p, u16 vid); +#define __br_get(__hook, __default, __args ...) \ + (__hook ? (__hook(__args)) : (__default)) /* QCA NSS ECM support */ #endif --- a/net/8021q/vlan_core.c +++ b/net/8021q/vlan_core.c @@ -72,6 +72,28 @@ bool vlan_do_receive(struct sk_buff **sk return true; } +/* QCA NSS ECM support - Start */ +/* Update the VLAN device with statistics from network offload engines */ +void __vlan_dev_update_accel_stats(struct net_device *dev, + struct rtnl_link_stats64 *nlstats) +{ + struct vlan_pcpu_stats *stats; + + if (!is_vlan_dev(dev)) + return; + + stats = per_cpu_ptr(vlan_dev_priv(dev)->vlan_pcpu_stats, 0); + + u64_stats_update_begin(&stats->syncp); + u64_stats_add(&stats->rx_packets, nlstats->rx_packets); + u64_stats_add(&stats->rx_bytes, nlstats->rx_bytes); + u64_stats_add(&stats->tx_packets, nlstats->tx_packets); + u64_stats_add(&stats->tx_bytes, nlstats->tx_bytes); + u64_stats_update_end(&stats->syncp); +} +EXPORT_SYMBOL(__vlan_dev_update_accel_stats); +/* QCA NSS ECM support - End */ + /* Must be invoked with rcu_read_lock. */ struct net_device *__vlan_find_dev_deep_rcu(struct net_device *dev, __be16 vlan_proto, u16 vlan_id) @@ -110,6 +132,15 @@ struct net_device *vlan_dev_real_dev(con } EXPORT_SYMBOL(vlan_dev_real_dev); +/* QCA NSS ECM support - Start */ +/* Caller is responsible to hold the reference of the returned device */ +struct net_device *vlan_dev_next_dev(const struct net_device *dev) +{ + return vlan_dev_priv(dev)->real_dev; +} +EXPORT_SYMBOL(vlan_dev_next_dev); +/* QCA NSS ECM support - End */ + u16 vlan_dev_vlan_id(const struct net_device *dev) { return vlan_dev_priv(dev)->vlan_id; --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -33,6 +33,20 @@ static const struct rhashtable_params br static struct kmem_cache *br_fdb_cache __read_mostly; +ATOMIC_NOTIFIER_HEAD(br_fdb_notifier_list); + +void br_fdb_register_notify(struct notifier_block *nb) +{ + atomic_notifier_chain_register(&br_fdb_notifier_list, nb); +} +EXPORT_SYMBOL_GPL(br_fdb_register_notify); + +void br_fdb_unregister_notify(struct notifier_block *nb) +{ + atomic_notifier_chain_unregister(&br_fdb_notifier_list, nb); +} +EXPORT_SYMBOL_GPL(br_fdb_unregister_notify); + int __init br_fdb_init(void) { br_fdb_cache = kmem_cache_create("bridge_fdb_cache", @@ -195,6 +209,25 @@ static void fdb_notify(struct net_bridge if (swdev_notify) br_switchdev_fdb_notify(br, fdb, type); + /* QCA NSS ECM support - Start */ + if (fdb->dst) { + int event; + struct br_fdb_event fdb_event; + + if (type == RTM_NEWNEIGH) + event = BR_FDB_EVENT_ADD; + else + event = BR_FDB_EVENT_DEL; + + fdb_event.dev = fdb->dst->dev; + ether_addr_copy(fdb_event.addr, fdb->key.addr.addr); + fdb_event.is_local = test_bit(BR_FDB_LOCAL, &fdb->flags); + atomic_notifier_call_chain(&br_fdb_notifier_list, + event, + (void *)&fdb_event); + } + /* QCA NSS ECM support - End */ + skb = nlmsg_new(fdb_nlmsg_size(), GFP_ATOMIC); if (skb == NULL) goto errout; @@ -519,6 +552,22 @@ out: spin_unlock_bh(&br->hash_lock); } +/* QCA NSS ECM support - Start */ +ATOMIC_NOTIFIER_HEAD(br_fdb_update_notifier_list); + +void br_fdb_update_register_notify(struct notifier_block *nb) +{ + atomic_notifier_chain_register(&br_fdb_update_notifier_list, nb); +} +EXPORT_SYMBOL_GPL(br_fdb_update_register_notify); + +void br_fdb_update_unregister_notify(struct notifier_block *nb) +{ + atomic_notifier_chain_unregister(&br_fdb_update_notifier_list, nb); +} +EXPORT_SYMBOL_GPL(br_fdb_update_unregister_notify); +/* QCA NSS ECM support - End */ + void br_fdb_cleanup(struct work_struct *work) { struct net_bridge *br = container_of(work, struct net_bridge, @@ -527,6 +576,7 @@ void br_fdb_cleanup(struct work_struct * unsigned long delay = hold_time(br); unsigned long work_delay = delay; unsigned long now = jiffies; + u8 mac_addr[6]; /* QCA NSS ECM support */ /* this part is tricky, in order to avoid blocking learning and * consequently forwarding, we rely on rcu to delete objects with @@ -553,8 +603,15 @@ void br_fdb_cleanup(struct work_struct * work_delay = min(work_delay, this_timer - now); } else { spin_lock_bh(&br->hash_lock); - if (!hlist_unhashed(&f->fdb_node)) + if (!hlist_unhashed(&f->fdb_node)) { + ether_addr_copy(mac_addr, f->key.addr.addr); fdb_delete(br, f, true); + /* QCA NSS ECM support - Start */ + atomic_notifier_call_chain( + &br_fdb_update_notifier_list, 0, + (void *)mac_addr); + /* QCA NSS ECM support - End */ + } spin_unlock_bh(&br->hash_lock); } } @@ -891,6 +948,12 @@ void br_fdb_update(struct net_bridge *br */ if (unlikely(test_bit(BR_FDB_LOCKED, &fdb->flags))) clear_bit(BR_FDB_LOCKED, &fdb->flags); + + /* QCA NSS ECM support - Start */ + atomic_notifier_call_chain( + &br_fdb_update_notifier_list, + 0, (void *)addr); + /* QCA NSS ECM support - End */ } if (unlikely(test_bit(BR_FDB_ADDED_BY_USER, &flags))) @@ -914,6 +977,64 @@ void br_fdb_update(struct net_bridge *br } } +/* QCA NSS ECM support - Start */ +/* Refresh FDB entries for bridge packets being forwarded by offload engines */ +void br_refresh_fdb_entry(struct net_device *dev, const char *addr) +{ + struct net_bridge_port *p = br_port_get_rcu(dev); + + if (!p || p->state == BR_STATE_DISABLED) + return; + + if (!is_valid_ether_addr(addr)) { + pr_info("bridge: Attempt to refresh with invalid ether address %pM\n", + addr); + return; + } + + rcu_read_lock(); + br_fdb_update(p->br, p, addr, 0, true); + rcu_read_unlock(); +} +EXPORT_SYMBOL_GPL(br_refresh_fdb_entry); + +/* Update timestamp of FDB entries for bridge packets being forwarded by offload engines */ +void br_fdb_entry_refresh(struct net_device *dev, const char *addr, __u16 vid) +{ + struct net_bridge_fdb_entry *fdb; + struct net_bridge_port *p = br_port_get_rcu(dev); + + if (!p || p->state == BR_STATE_DISABLED) + return; + + rcu_read_lock(); + fdb = fdb_find_rcu(&p->br->fdb_hash_tbl, addr, vid); + if (likely(fdb)) { + fdb->updated = jiffies; + } + rcu_read_unlock(); +} +EXPORT_SYMBOL_GPL(br_fdb_entry_refresh); + +/* Look up the MAC address in the device's bridge fdb table */ +struct net_bridge_fdb_entry *br_fdb_has_entry(struct net_device *dev, + const char *addr, __u16 vid) +{ + struct net_bridge_port *p = br_port_get_rcu(dev); + struct net_bridge_fdb_entry *fdb; + + if (!p || p->state == BR_STATE_DISABLED) + return NULL; + + rcu_read_lock(); + fdb = fdb_find_rcu(&p->br->fdb_hash_tbl, addr, vid); + rcu_read_unlock(); + + return fdb; +} +EXPORT_SYMBOL_GPL(br_fdb_has_entry); + +/* QCA NSS ECM support - End */ /* Dump information about entries, in response to GETNEIGH */ int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -26,6 +26,12 @@ #include "br_private.h" +/* QCA NSS ECM support - Start */ +/* Hook for external forwarding logic */ +br_port_dev_get_hook_t __rcu *br_port_dev_get_hook __read_mostly; +EXPORT_SYMBOL_GPL(br_port_dev_get_hook); +/* QCA NSS ECM support - End */ + /* * Determine initial path cost based on speed. * using recommendations from 802.1d standard @@ -697,6 +703,8 @@ int br_add_if(struct net_bridge *br, str kobject_uevent(&p->kobj, KOBJ_ADD); + call_netdevice_notifiers(NETDEV_BR_JOIN, dev); /* QCA NSS ECM support */ + return 0; err6: @@ -732,6 +740,8 @@ int br_del_if(struct net_bridge *br, str if (!p || p->br != br) return -EINVAL; + call_netdevice_notifiers(NETDEV_BR_LEAVE, dev); /* QCA NSS ECM support */ + /* Since more than one interface can be attached to a bridge, * there still maybe an alternate path for netconsole to use; * therefore there is no reason for a NETDEV_RELEASE event. @@ -775,3 +785,97 @@ bool br_port_flag_is_set(const struct ne return p->flags & flag; } EXPORT_SYMBOL_GPL(br_port_flag_is_set); + +/* br_port_dev_get() + * If a skb is provided, and the br_port_dev_get_hook_t hook exists, + * use that to try and determine the egress port for that skb. + * If not, or no egress port could be determined, use the given addr + * to identify the port to which it is reachable, + * returing a reference to the net device associated with that port. + * + * NOTE: Return NULL if given dev is not a bridge or the mac has no + * associated port. + */ +struct net_device *br_port_dev_get(struct net_device *dev, unsigned char *addr, + struct sk_buff *skb, + unsigned int cookie) +{ + struct net_bridge_fdb_entry *fdbe; + struct net_bridge *br; + struct net_device *netdev = NULL; + + /* Is this a bridge? */ + if (!(dev->priv_flags & IFF_EBRIDGE)) + return NULL; + + rcu_read_lock(); + + /* If the hook exists and the skb isn't NULL, try and get the port */ + if (skb) { + br_port_dev_get_hook_t *port_dev_get_hook; + + port_dev_get_hook = rcu_dereference(br_port_dev_get_hook); + if (port_dev_get_hook) { + struct net_bridge_port *pdst = + __br_get(port_dev_get_hook, NULL, dev, skb, + addr, cookie); + if (pdst) { + dev_hold(pdst->dev); + netdev = pdst->dev; + goto out; + } + } + } + + /* Either there is no hook, or can't + * determine the port to use - fall back to using FDB + */ + + br = netdev_priv(dev); + + /* Lookup the fdb entry and get reference to the port dev */ + fdbe = br_fdb_find_rcu(br, addr, 0); + if (fdbe && fdbe->dst) { + netdev = fdbe->dst->dev; /* port device */ + dev_hold(netdev); + } +out: + rcu_read_unlock(); + return netdev; +} +EXPORT_SYMBOL_GPL(br_port_dev_get); + +/* Update bridge statistics for bridge packets processed by offload engines */ +void br_dev_update_stats(struct net_device *dev, + struct rtnl_link_stats64 *nlstats) +{ + struct pcpu_sw_netstats *tstats; + + /* Is this a bridge? */ + if (!(dev->priv_flags & IFF_EBRIDGE)) + return; + + tstats = this_cpu_ptr(dev->tstats); + + u64_stats_update_begin(&tstats->syncp); + u64_stats_add(&tstats->rx_packets, nlstats->rx_packets); + u64_stats_add(&tstats->rx_bytes, nlstats->rx_bytes); + u64_stats_add(&tstats->tx_packets, nlstats->tx_packets); + u64_stats_add(&tstats->tx_bytes, nlstats->tx_bytes); + u64_stats_update_end(&tstats->syncp); +} +EXPORT_SYMBOL_GPL(br_dev_update_stats); + +/* QCA NSS ECM support - Start */ +/* API to know if hairpin feature is enabled/disabled on this bridge port */ +bool br_is_hairpin_enabled(struct net_device *dev) +{ + struct net_bridge_port *port = br_port_get_check_rcu(dev); + + if (likely(port)) + return port->flags & BR_HAIRPIN_MODE; + return false; +} +EXPORT_SYMBOL_GPL(br_is_hairpin_enabled); + +/* QCA NSS ECM support - End */ --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -1275,6 +1275,22 @@ static void neigh_update_hhs(struct neig } } +/* QCA NSS ECM support - Start */ +ATOMIC_NOTIFIER_HEAD(neigh_mac_update_notifier_list); + +void neigh_mac_update_register_notify(struct notifier_block *nb) +{ + atomic_notifier_chain_register(&neigh_mac_update_notifier_list, nb); +} +EXPORT_SYMBOL_GPL(neigh_mac_update_register_notify); + +void neigh_mac_update_unregister_notify(struct notifier_block *nb) +{ + atomic_notifier_chain_unregister(&neigh_mac_update_notifier_list, nb); +} +EXPORT_SYMBOL_GPL(neigh_mac_update_unregister_notify); +/* QCA NSS ECM support - End */ + /* Generic update routine. -- lladdr is new lladdr or NULL, if it is not supplied. -- new is new state. @@ -1303,6 +1319,7 @@ static int __neigh_update(struct neighbo struct net_device *dev; int err, notify = 0; u8 old; + struct neigh_mac_update nmu; /* QCA NSS ECM support */ trace_neigh_update(neigh, lladdr, new, flags, nlmsg_pid); @@ -1317,7 +1334,10 @@ static int __neigh_update(struct neighbo new = old; goto out; } - if (!(flags & NEIGH_UPDATE_F_ADMIN) && + + memset(&nmu, 0, sizeof(struct neigh_mac_update)); /* QCA NSS ECM support */ + + if (!(flags & NEIGH_UPDATE_F_ADMIN) && (old & (NUD_NOARP | NUD_PERMANENT))) goto out; @@ -1354,7 +1374,12 @@ static int __neigh_update(struct neighbo - compare new & old - if they are different, check override flag */ - if ((old & NUD_VALID) && + /* QCA NSS ECM update - Start */ + memcpy(nmu.old_mac, neigh->ha, dev->addr_len); + memcpy(nmu.update_mac, lladdr, dev->addr_len); + /* QCA NSS ECM update - End */ + + if ((old & NUD_VALID) && !memcmp(lladdr, neigh->ha, dev->addr_len)) lladdr = neigh->ha; } else { @@ -1476,8 +1501,11 @@ out: neigh_update_gc_list(neigh); if (managed_update) neigh_update_managed_list(neigh); - if (notify) + if (notify) { neigh_update_notify(neigh, nlmsg_pid); + atomic_notifier_call_chain(&neigh_mac_update_notifier_list, 0, + (struct neigh_mac_update *)&nmu); /* QCA NSS ECM support */ + } trace_neigh_update_done(neigh, err); return err; } --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -1211,6 +1211,9 @@ static bool fib_valid_key_len(u32 key, u static void fib_remove_alias(struct trie *t, struct key_vector *tp, struct key_vector *l, struct fib_alias *old); +/* Define route change notification chain. */ +static BLOCKING_NOTIFIER_HEAD(iproute_chain); /* QCA NSS ECM support */ + /* Caller must hold RTNL. */ int fib_table_insert(struct net *net, struct fib_table *tb, struct fib_config *cfg, struct netlink_ext_ack *extack) @@ -1404,6 +1407,9 @@ int fib_table_insert(struct net *net, st rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen, new_fa->tb_id, &cfg->fc_nlinfo, nlflags); succeeded: + blocking_notifier_call_chain(&iproute_chain, + RTM_NEWROUTE, fi); + return 0; out_remove_new_fa: @@ -1775,6 +1781,9 @@ int fib_table_delete(struct net *net, st if (fa_to_delete->fa_state & FA_S_ACCESSED) rt_cache_flush(cfg->fc_nlinfo.nl_net); + blocking_notifier_call_chain(&iproute_chain, + RTM_DELROUTE, fa_to_delete->fa_info); + fib_release_info(fa_to_delete->fa_info); alias_free_mem_rcu(fa_to_delete); return 0; @@ -2407,6 +2416,20 @@ void __init fib_trie_init(void) 0, SLAB_PANIC | SLAB_ACCOUNT, NULL); } +/* QCA NSS ECM support - Start */ +int ip_rt_register_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&iproute_chain, nb); +} +EXPORT_SYMBOL(ip_rt_register_notifier); + +int ip_rt_unregister_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&iproute_chain, nb); +} +EXPORT_SYMBOL(ip_rt_unregister_notifier); +/* QCA NSS ECM support - End */ + struct fib_table *fib_trie_table(u32 id, struct fib_table *alias) { struct fib_table *tb; --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -666,6 +666,7 @@ void ndisc_send_ns(struct net_device *de if (skb) ndisc_send_skb(skb, daddr, saddr); } +EXPORT_SYMBOL(ndisc_send_ns); void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr, const struct in6_addr *daddr) --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -197,6 +197,9 @@ static void rt6_uncached_list_flush_dev( } } +/* Define route change notification chain. */ +ATOMIC_NOTIFIER_HEAD(ip6route_chain); /* QCA NSS ECM support */ + static inline const void *choose_neigh_daddr(const struct in6_addr *p, struct sk_buff *skb, const void *daddr) @@ -3864,6 +3867,10 @@ int ip6_route_add(struct fib6_config *cf return PTR_ERR(rt); err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, extack); + if (!err) + atomic_notifier_call_chain(&ip6route_chain, + RTM_NEWROUTE, rt); + fib6_info_release(rt); return err; @@ -3885,6 +3892,9 @@ static int __ip6_del_rt(struct fib6_info err = fib6_del(rt, info); spin_unlock_bh(&table->tb6_lock); + if (!err) + atomic_notifier_call_chain(&ip6route_chain, + RTM_DELROUTE, rt); out: fib6_info_release(rt); return err; @@ -6329,6 +6339,20 @@ static int ip6_route_dev_notify(struct n return NOTIFY_OK; } +/* QCA NSS ECM support - Start */ +int rt6_register_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_register(&ip6route_chain, nb); +} +EXPORT_SYMBOL(rt6_register_notifier); + +int rt6_unregister_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_unregister(&ip6route_chain, nb); +} +EXPORT_SYMBOL(rt6_unregister_notifier); +/* QCA NSS ECM support - End */ + /* * /proc */ --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1673,6 +1673,7 @@ const char *netdev_cmd_to_name(enum netd N(PRE_CHANGEADDR) N(OFFLOAD_XSTATS_ENABLE) N(OFFLOAD_XSTATS_DISABLE) N(OFFLOAD_XSTATS_REPORT_USED) N(OFFLOAD_XSTATS_REPORT_DELTA) N(XDP_FEAT_CHANGE) + N(BR_JOIN) N(BR_LEAVE) } #undef N return "UNKNOWN_NETDEV_EVENT"; --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -1002,6 +1002,7 @@ void inet6_ifa_finish_destroy(struct ine kfree_rcu(ifp, rcu); } +EXPORT_SYMBOL(inet6_ifa_finish_destroy); static void ipv6_link_dev_addr(struct inet6_dev *idev, struct inet6_ifaddr *ifp) --- a/include/net/vxlan.h +++ b/include/net/vxlan.h @@ -440,6 +440,15 @@ static inline __be32 vxlan_compute_rco(u return vni_field; } +/* + * vxlan_get_vni() + * Returns the vni corresponding to tunnel + */ +static inline u32 vxlan_get_vni(struct vxlan_dev *vxlan_tun) +{ + return be32_to_cpu(vxlan_tun->cfg.vni); +} + static inline unsigned short vxlan_get_sk_family(struct vxlan_sock *vs) { return vs->sock->sk->sk_family; --- a/include/uapi/linux/in.h +++ b/include/uapi/linux/in.h @@ -63,6 +63,8 @@ enum { #define IPPROTO_MTP IPPROTO_MTP IPPROTO_BEETPH = 94, /* IP option pseudo header for BEET */ #define IPPROTO_BEETPH IPPROTO_BEETPH + IPPROTO_ETHERIP = 97, /* ETHERIP protocol number */ +#define IPPROTO_ETHERIP IPPROTO_ETHERIP IPPROTO_ENCAP = 98, /* Encapsulation Header */ #define IPPROTO_ENCAP IPPROTO_ENCAP IPPROTO_PIM = 103, /* Protocol Independent Multicast */ @@ -327,7 +329,7 @@ struct sockaddr_in { #endif /* contains the htonl type stuff.. */ -#include +#include #endif /* _UAPI_LINUX_IN_H */ --- a/tools/include/uapi/linux/in.h +++ b/tools/include/uapi/linux/in.h @@ -63,6 +63,8 @@ enum { #define IPPROTO_MTP IPPROTO_MTP IPPROTO_BEETPH = 94, /* IP option pseudo header for BEET */ #define IPPROTO_BEETPH IPPROTO_BEETPH + IPPROTO_ETHERIP = 97, /* ETHERIP protocol number */ +#define IPPROTO_ETHERIP IPPROTO_ETHERIP IPPROTO_ENCAP = 98, /* Encapsulation Header */ #define IPPROTO_ENCAP IPPROTO_ENCAP IPPROTO_PIM = 103, /* Protocol Independent Multicast */ @@ -327,7 +329,7 @@ struct sockaddr_in { #endif /* contains the htonl type stuff.. */ -#include +#include #endif /* _UAPI_LINUX_IN_H */ --- a/net/netfilter/nf_conntrack_ecache.c +++ b/net/netfilter/nf_conntrack_ecache.c @@ -266,7 +266,6 @@ void nf_conntrack_register_notifier(stru mutex_lock(&nf_ct_ecache_mutex); notify = rcu_dereference_protected(net->ct.nf_conntrack_event_cb, lockdep_is_held(&nf_ct_ecache_mutex)); - WARN_ON_ONCE(notify); rcu_assign_pointer(net->ct.nf_conntrack_event_cb, new); mutex_unlock(&nf_ct_ecache_mutex); } --- a/include/net/netns/conntrack.h +++ b/include/net/netns/conntrack.h @@ -26,6 +26,7 @@ struct nf_tcp_net { unsigned int timeouts[TCP_CONNTRACK_TIMEOUT_MAX]; u8 tcp_loose; u8 tcp_be_liberal; + u8 tcp_no_window_check; u8 tcp_max_retrans; u8 tcp_ignore_invalid_rst; #if IS_ENABLED(CONFIG_NF_FLOW_TABLE) --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -515,11 +515,15 @@ tcp_in_window(struct nf_conn *ct, enum i struct ip_ct_tcp *state = &ct->proto.tcp; struct ip_ct_tcp_state *sender = &state->seen[dir]; struct ip_ct_tcp_state *receiver = &state->seen[!dir]; + const struct nf_tcp_net *tn = nf_tcp_pernet(nf_ct_net(ct)); __u32 seq, ack, sack, end, win, swin; bool in_recv_win, seq_ok; s32 receiver_offset; u16 win_raw; + if (tn->tcp_no_window_check) + return NFCT_TCP_ACCEPT; + /* * Get the required data from the packet. */ @@ -1285,7 +1289,7 @@ int nf_conntrack_tcp_packet(struct nf_co IP_CT_TCP_FLAG_DATA_UNACKNOWLEDGED && timeouts[new_state] > timeouts[TCP_CONNTRACK_UNACK]) timeout = timeouts[TCP_CONNTRACK_UNACK]; - else if (ct->proto.tcp.last_win == 0 && + else if (!tn->tcp_no_window_check && ct->proto.tcp.last_win == 0 && timeouts[new_state] > timeouts[TCP_CONNTRACK_RETRANS]) timeout = timeouts[TCP_CONNTRACK_RETRANS]; else @@ -1601,6 +1605,9 @@ void nf_conntrack_tcp_init_net(struct ne */ tn->tcp_be_liberal = 0; + /* Skip Windows Check */ + tn->tcp_no_window_check = 0; + /* If it's non-zero, we turn off RST sequence number check */ tn->tcp_ignore_invalid_rst = 0; --- a/net/netfilter/nf_conntrack_standalone.c +++ b/net/netfilter/nf_conntrack_standalone.c @@ -633,6 +633,7 @@ enum nf_ct_sysctl_index { #endif NF_SYSCTL_CT_PROTO_TCP_LOOSE, NF_SYSCTL_CT_PROTO_TCP_LIBERAL, + NF_SYSCTL_CT_PROTO_TCP_NO_WINDOW_CHECK, NF_SYSCTL_CT_PROTO_TCP_IGNORE_INVALID_RST, NF_SYSCTL_CT_PROTO_TCP_MAX_RETRANS, NF_SYSCTL_CT_PROTO_TIMEOUT_UDP, @@ -840,6 +841,14 @@ static struct ctl_table nf_ct_sysctl_tab .extra1 = SYSCTL_ZERO, .extra2 = SYSCTL_ONE, }, + [NF_SYSCTL_CT_PROTO_TCP_NO_WINDOW_CHECK] = { + .procname = "nf_conntrack_tcp_no_window_check", + .maxlen = sizeof(u8), + .mode = 0644, + .proc_handler = proc_dou8vec_minmax, + .extra1 = SYSCTL_ZERO, + .extra2 = SYSCTL_ONE, + }, [NF_SYSCTL_CT_PROTO_TCP_IGNORE_INVALID_RST] = { .procname = "nf_conntrack_tcp_ignore_invalid_rst", .maxlen = sizeof(u8), @@ -1050,6 +1059,7 @@ static void nf_conntrack_standalone_init XASSIGN(LOOSE, &tn->tcp_loose); XASSIGN(LIBERAL, &tn->tcp_be_liberal); + XASSIGN(NO_WINDOW_CHECK, &tn->tcp_no_window_check); XASSIGN(MAX_RETRANS, &tn->tcp_max_retrans); XASSIGN(IGNORE_INVALID_RST, &tn->tcp_ignore_invalid_rst); #undef XASSIGN