add receiving function
process unicast frames in receiving function
multicast frame are not handled from the moment

Signed-off-by: Ahmed Amamou <ah...@gandi.net>
Signed-off-by: Kamel Haddadou <ka...@gandi.net>
Signed-off-by: William Dauchy <will...@gandi.net>
---
 net/bridge/rbr.c | 233 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 232 insertions(+), 1 deletion(-)

diff --git a/net/bridge/rbr.c b/net/bridge/rbr.c
index 8081b20..c04c05a 100644
--- a/net/bridge/rbr.c
+++ b/net/bridge/rbr.c
@@ -243,6 +243,237 @@ static void rbr_encaps(struct sk_buff *skb, u16 
egressnick, u16 vid)
        kfree_skb(skb);
 }
 
+static void rbr_decap_finish(struct sk_buff *skb, u16 vid)
+{
+       struct net_bridge *br;
+       struct net_bridge_port *p;
+       const unsigned char *dest = eth_hdr(skb)->h_dest;
+       struct net_bridge_fdb_entry *dst;
+
+       p = br_port_get_rcu(skb->dev);
+       br = p->br;
+       dst = __br_fdb_get(br, dest, vid);
+       if (likely(dst))
+               br_deliver(dst->dst, skb);
+       else
+               /* destination unknown flood on all access ports */
+               br_flood_deliver_flags(br, skb, true, TRILL_FLAG_ACCESS);
+}
+
+static void rbr_decaps(struct net_bridge_port *p,
+                      struct sk_buff *skb, size_t trhsize, u16 vid)
+{
+       struct trill_hdr *trh;
+       struct ethhdr *hdr;
+
+       if (unlikely(!p))
+               goto rbr_decaps_drop;
+       trh = (struct trill_hdr *)skb->data;
+       if (trhsize >= sizeof(*trh))
+               skb_pull(skb, sizeof(*trh));
+       else
+               goto rbr_decaps_drop;
+       trhsize -= sizeof(*trh);
+       skb_reset_mac_header(skb);      /* instead of the inner one */
+       skb->protocol = eth_hdr(skb)->h_proto;
+       hdr = (struct ethhdr *)skb->data;
+       skb_pull(skb, ETH_HLEN);
+       skb_reset_network_header(skb);
+       if (skb->encapsulation)
+               skb->encapsulation = 0;
+       br_fdb_update_nick(p->br, p, hdr->h_source, vid, false,
+                          trh->th_ingressnick);
+       rbr_decap_finish(skb, vid);
+       return;
+ rbr_decaps_drop:
+       if (likely(p && p->br))
+               p->br->dev->stats.rx_dropped++;
+       kfree_skb(skb);
+}
+
+static void rbr_recv(struct sk_buff *skb, u16 vid)
+{
+       u16 local_nick, dtnick, adjnick, idx;
+       struct rbr *rbr;
+       struct trill_hdr *trh;
+       size_t trhsize;
+       struct net_bridge_port *p;
+       u16 trill_flags;
+       struct sk_buff *skb2;
+       struct rbr_node *dest = NULL;
+       struct rbr_node *source_node = NULL;
+       struct rbr_node *adj = NULL;
+
+       p = br_port_get_rcu(skb->dev);
+       if (unlikely(!p)) {
+               pr_warn_ratelimited("rbr_recv: port error\n");
+               goto recv_drop;
+       }
+       rbr = p->br->rbr;
+       /* For trill frame the outer mac destination must correspond
+        * to localhost address, if not frame must be discarded
+        * such scenario is possible when switch flood frames on all ports
+        * if frame are not discarded they will loop until reaching the
+        * hop_count limit
+        */
+       if (memcmp(p->dev->dev_addr, eth_hdr(skb)->h_dest, ETH_ALEN))
+               goto recv_drop;
+       trh = (struct trill_hdr *)skb->data;
+       trill_flags = ntohs(trh->th_flags);
+       trhsize = sizeof(*trh) + trill_get_optslen(trill_flags);
+       if (unlikely(skb->len < trhsize + ETH_HLEN)) {
+               pr_warn_ratelimited
+                   ("rbr_recv: sk_buff len is less then minimal len\n");
+               goto recv_drop;
+       }
+       /* seems to be a valid TRILL frame,
+        * check if TRILL header can be pulled
+        * before proceeding
+        */
+       if (unlikely(!pskb_may_pull(skb, trhsize + ETH_HLEN)))
+               goto recv_drop;
+
+       /* WARNING SKB structure may be changed by pskb_may_pull
+        * reassign trh pointer before continuing any further
+        */
+       trh = (struct trill_hdr *)skb->data;
+
+       if (!skb->encapsulation) {
+               skb_pull(skb, trhsize + ETH_HLEN);
+               skb_reset_inner_headers(skb);
+               skb->encapsulation = 1;
+               skb_push(skb, trhsize + ETH_HLEN);
+       }
+       if (unlikely(!VALID_NICK(trh->th_ingressnick) ||
+                    !VALID_NICK(trh->th_egressnick))) {
+               pr_warn_ratelimited("rbr_recv: invalid nickname\n");
+               goto recv_drop;
+       }
+       if (unlikely(trill_get_version(trill_flags) != TRILL_PROTOCOL_VERS)) {
+               pr_warn_ratelimited("rbr_recv: not the same trill version\n");
+               goto recv_drop;
+       }
+       local_nick = rbr->nick;
+       dtnick = rbr->treeroot;
+       if (unlikely(trh->th_ingressnick == local_nick)) {
+               pr_warn_ratelimited
+                   ("rbr_recv:looping back frame check your config\n");
+               goto recv_drop;
+       }
+
+       if (!trill_get_multidest(trill_flags)) {
+               /* ntohs not needed as the 2 are in the same bit form */
+               if (trh->th_egressnick == trh->th_ingressnick) {
+                       pr_warn_ratelimited
+                           ("rbr_recv: egressnick == ingressnick\n");
+                       goto recv_drop;
+               }
+               if (trh->th_egressnick == local_nick) {
+                       rbr_decaps(p, skb, trhsize, vid);
+               } else if (likely(trill_get_hopcount(trill_flags))) {
+                       br_fdb_update(p->br, p, eth_hdr(skb)->h_source,
+                                     vid, false);
+                       /* TODO simple forwarding */
+               } else {
+                       pr_warn_ratelimited("rbr_recv: hop count limit 
reached\n");
+                       goto recv_drop;
+               }
+               return;
+       }
+
+       /* Multi-destination frame:
+        * Check if received multi-destination frame from an
+        * adjacency in the distribution tree rooted at egress nick
+        * indicated in the frame header
+        */
+       dest = rbr_find_node(rbr, trh->th_egressnick);
+       if (unlikely(!dest)) {
+               pr_warn_ratelimited
+                   ("rbr_recv: mulicast  with unknown destination\n");
+               goto recv_drop;
+       }
+       for (idx = 0; idx < dest->rbr_ni->adjcount; idx++) {
+               adjnick = RBR_NI_ADJNICK(dest->rbr_ni, idx);
+               adj = rbr_find_node(rbr, adjnick);
+               if (unlikely(!adj || !adj->rbr_ni))
+                       continue;
+               if (memcmp(adj->rbr_ni->adjsnpa, eth_hdr(skb)->h_source,
+                          ETH_ALEN) == 0) {
+                       rbr_node_put(adj);
+                       break;
+               }
+               rbr_node_put(adj);
+       }
+
+       if (unlikely(idx >= dest->rbr_ni->adjcount)) {
+               pr_warn_ratelimited("rbr_recv: multicast unknown mac source\n");
+               rbr_node_put(dest);
+               goto recv_drop;
+       }
+
+       /* Reverse path forwarding check.
+        * Check if the ingress RBridge  that has forwarded
+        * the frame advertised the use of the distribution tree specified
+        * in the egress nick
+        */
+       source_node = rbr_find_node(rbr, trh->th_ingressnick);
+       if (unlikely(!source_node)) {
+               pr_warn_ratelimited
+                   ("rbr_recv: reverse path forwarding check failed\n");
+               rbr_node_put(dest);
+               goto recv_drop;
+       }
+       for (idx = 0; idx < source_node->rbr_ni->dtrootcount; idx++) {
+               if (RBR_NI_DTROOTNICK(source_node->rbr_ni, idx) ==
+                   trh->th_egressnick)
+                       break;
+       }
+
+       if (idx >= source_node->rbr_ni->dtrootcount) {
+               /* Allow receipt of forwarded frame with the highest
+                * tree root RBridge as the egress RBridge when the
+                * ingress RBridge has not advertised the use of any
+                * distribution trees.
+                */
+               if (source_node->rbr_ni->dtrootcount != 0 ||
+                   trh->th_egressnick != dtnick) {
+                       rbr_node_put(source_node);
+                       rbr_node_put(dest);
+                       goto recv_drop;
+               }
+       }
+
+       /* Check hop count before doing any forwarding */
+       if (unlikely(trill_get_hopcount(trill_flags) == 0)) {
+               pr_warn_ratelimited
+                   ("rbr_recv: multicast hop count limit reached\n");
+               rbr_node_put(dest);
+               goto recv_drop;
+       }
+       /* Forward frame using the distribution tree specified by egress nick */
+       rbr_node_put(source_node);
+       rbr_node_put(dest);
+
+       /* skb2 will be multi forwarded and skb will be locally decaps */
+       skb2 = skb_clone(skb, GFP_ATOMIC);
+       if (unlikely(!skb2)) {
+               p->br->dev->stats.tx_dropped++;
+               pr_warn_ratelimited("rbr_recv: multicast skb_clone failed\n");
+               goto recv_drop;
+       }
+
+       /* TODO multi forwarding */
+
+       /* Send de-capsulated frame locally */
+       rbr_decaps(p, skb, trhsize, vid);
+
+       return;
+ recv_drop:
+       if (likely(p && p->br))
+               p->br->dev->stats.rx_dropped++;
+       kfree_skb(skb);
+}
+
 /* handling function hook allow handling
  * a frame upon reception called via
  * br_handle_frame_hook = rbr_handle_frame
@@ -310,7 +541,7 @@ rx_handler_result_t rbr_handle_frame(struct sk_buff **pskb)
                         * if destined to access port
                         * or trill forward to next hop
                         */
-                       /* TODO */
+                       rbr_recv(skb, vid);
                        return RX_HANDLER_CONSUMED;
                }
                /* packet is destinated to localhost */
-- 
2.1.4

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to