diff options
author | Stephen Hemminger <shemminger@osdl.org> | 2006-05-25 15:10:37 -0700 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2006-06-17 21:26:08 -0700 |
commit | bc0e646796928918e45b6465e02616f2fe65c3c1 (patch) | |
tree | 8c7a52563188e7e4168cd98b58b980c8096c02ce | |
parent | 8f182b494f87799d6ae20a1401825c516da46081 (diff) |
[LLC]: add multicast support for datagrams
Allow mulitcast reception of datagrams (similar to UDP).
All sockets bound to the same SAP receive a clone.
Signed-off-by: Stephen Hemminger <shemminger@osdl.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/net/llc_if.h | 4 | ||||
-rw-r--r-- | net/llc/llc_sap.c | 59 |
2 files changed, 55 insertions, 8 deletions
diff --git a/include/net/llc_if.h b/include/net/llc_if.h index a05d04ac451..c608812a8e8 100644 --- a/include/net/llc_if.h +++ b/include/net/llc_if.h @@ -79,6 +79,10 @@ static inline int llc_addrany(const struct llc_addr *addr) return llc_mac_null(addr->mac) && !addr->lsap; } +static inline int llc_mac_multicast(const u8 *mac) +{ + return is_multicast_ether_addr(mac); +} /** * llc_mac_match - determines if two mac addresses are the same * @mac1: First mac address to compare. diff --git a/net/llc/llc_sap.c b/net/llc/llc_sap.c index 4029ceee9b9..20c4eb5c1ac 100644 --- a/net/llc/llc_sap.c +++ b/net/llc/llc_sap.c @@ -282,7 +282,7 @@ static void llc_sap_rcv(struct llc_sap *sap, struct sk_buff *skb) * mac, and local sap. Returns pointer for socket found, %NULL otherwise. */ static struct sock *llc_lookup_dgram(struct llc_sap *sap, - struct llc_addr *laddr) + const struct llc_addr *laddr) { struct sock *rc; struct hlist_node *node; @@ -304,19 +304,62 @@ found: return rc; } +/** + * llc_sap_mcast - Deliver multicast PDU's to all matching datagram sockets. + * @sap: SAP + * @laddr: address of local LLC (MAC + SAP) + * + * Search socket list of the SAP and finds connections with same sap. + * Deliver clone to each. + */ +static void llc_sap_mcast(struct llc_sap *sap, + const struct llc_addr *laddr, + struct sk_buff *skb) +{ + struct sock *sk; + struct hlist_node *node; + + read_lock_bh(&sap->sk_list.lock); + sk_for_each(sk, node, &sap->sk_list.list) { + struct llc_sock *llc = llc_sk(sk); + struct sk_buff *skb1; + + if (sk->sk_type != SOCK_DGRAM) + continue; + + if (llc->laddr.lsap != laddr->lsap) + continue; + + skb1 = skb_clone(skb, GFP_ATOMIC); + if (!skb1) + break; + + sock_hold(sk); + skb_set_owner_r(skb1, sk); + llc_sap_rcv(sap, skb1); + sock_put(sk); + } + read_unlock_bh(&sap->sk_list.lock); +} + + void llc_sap_handler(struct llc_sap *sap, struct sk_buff *skb) { struct llc_addr laddr; - struct sock *sk; llc_pdu_decode_da(skb, laddr.mac); llc_pdu_decode_dsap(skb, &laddr.lsap); - sk = llc_lookup_dgram(sap, &laddr); - if (sk) { - skb_set_owner_r(skb, sk); - llc_sap_rcv(sap, skb); - sock_put(sk); - } else + if (llc_mac_multicast(laddr.mac)) { + llc_sap_mcast(sap, &laddr, skb); kfree_skb(skb); + } else { + struct sock *sk = llc_lookup_dgram(sap, &laddr); + if (sk) { + skb_set_owner_r(skb, sk); + llc_sap_rcv(sap, skb); + sock_put(sk); + } else + kfree_skb(skb); + } } |