aboutsummaryrefslogtreecommitdiffstats
path: root/net/llc
diff options
context:
space:
mode:
Diffstat (limited to 'net/llc')
-rw-r--r--net/llc/af_llc.c74
-rw-r--r--net/llc/llc_c_ac.c3
-rw-r--r--net/llc/llc_conn.c147
-rw-r--r--net/llc/llc_core.c53
-rw-r--r--net/llc/llc_if.c1
-rw-r--r--net/llc/llc_input.c1
-rw-r--r--net/llc/llc_output.c45
-rw-r--r--net/llc/llc_proc.c69
-rw-r--r--net/llc/llc_sap.c114
-rw-r--r--net/llc/llc_station.c1
-rw-r--r--net/llc/sysctl_net_llc.c25
11 files changed, 338 insertions, 195 deletions
diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c
index 7aa4fd170104..2db6a9f75913 100644
--- a/net/llc/af_llc.c
+++ b/net/llc/af_llc.c
@@ -25,6 +25,7 @@
25#include <linux/module.h> 25#include <linux/module.h>
26#include <linux/rtnetlink.h> 26#include <linux/rtnetlink.h>
27#include <linux/init.h> 27#include <linux/init.h>
28#include <linux/slab.h>
28#include <net/llc.h> 29#include <net/llc.h>
29#include <net/llc_sap.h> 30#include <net/llc_sap.h>
30#include <net/llc_pdu.h> 31#include <net/llc_pdu.h>
@@ -47,6 +48,10 @@ static int llc_ui_wait_for_busy_core(struct sock *sk, long timeout);
47#define dprintk(args...) 48#define dprintk(args...)
48#endif 49#endif
49 50
51/* Maybe we'll add some more in the future. */
52#define LLC_CMSG_PKTINFO 1
53
54
50/** 55/**
51 * llc_ui_next_link_no - return the next unused link number for a sap 56 * llc_ui_next_link_no - return the next unused link number for a sap
52 * @sap: Address of sap to get link number from. 57 * @sap: Address of sap to get link number from.
@@ -136,18 +141,22 @@ static struct proto llc_proto = {
136 .name = "LLC", 141 .name = "LLC",
137 .owner = THIS_MODULE, 142 .owner = THIS_MODULE,
138 .obj_size = sizeof(struct llc_sock), 143 .obj_size = sizeof(struct llc_sock),
144 .slab_flags = SLAB_DESTROY_BY_RCU,
139}; 145};
140 146
141/** 147/**
142 * llc_ui_create - alloc and init a new llc_ui socket 148 * llc_ui_create - alloc and init a new llc_ui socket
149 * @net: network namespace (must be default network)
143 * @sock: Socket to initialize and attach allocated sk to. 150 * @sock: Socket to initialize and attach allocated sk to.
144 * @protocol: Unused. 151 * @protocol: Unused.
152 * @kern: on behalf of kernel or userspace
145 * 153 *
146 * Allocate and initialize a new llc_ui socket, validate the user wants a 154 * Allocate and initialize a new llc_ui socket, validate the user wants a
147 * socket type we have available. 155 * socket type we have available.
148 * Returns 0 upon success, negative upon failure. 156 * Returns 0 upon success, negative upon failure.
149 */ 157 */
150static int llc_ui_create(struct net *net, struct socket *sock, int protocol) 158static int llc_ui_create(struct net *net, struct socket *sock, int protocol,
159 int kern)
151{ 160{
152 struct sock *sk; 161 struct sock *sk;
153 int rc = -ESOCKTNOSUPPORT; 162 int rc = -ESOCKTNOSUPPORT;
@@ -155,7 +164,7 @@ static int llc_ui_create(struct net *net, struct socket *sock, int protocol)
155 if (!capable(CAP_NET_RAW)) 164 if (!capable(CAP_NET_RAW))
156 return -EPERM; 165 return -EPERM;
157 166
158 if (net != &init_net) 167 if (!net_eq(net, &init_net))
159 return -EAFNOSUPPORT; 168 return -EAFNOSUPPORT;
160 169
161 if (likely(sock->type == SOCK_DGRAM || sock->type == SOCK_STREAM)) { 170 if (likely(sock->type == SOCK_DGRAM || sock->type == SOCK_STREAM)) {
@@ -189,10 +198,8 @@ static int llc_ui_release(struct socket *sock)
189 llc->laddr.lsap, llc->daddr.lsap); 198 llc->laddr.lsap, llc->daddr.lsap);
190 if (!llc_send_disc(sk)) 199 if (!llc_send_disc(sk))
191 llc_ui_wait_for_disc(sk, sk->sk_rcvtimeo); 200 llc_ui_wait_for_disc(sk, sk->sk_rcvtimeo);
192 if (!sock_flag(sk, SOCK_ZAPPED)) { 201 if (!sock_flag(sk, SOCK_ZAPPED))
193 llc_sap_put(llc->sap);
194 llc_sap_remove_socket(llc->sap, sk); 202 llc_sap_remove_socket(llc->sap, sk);
195 }
196 release_sock(sk); 203 release_sock(sk);
197 if (llc->dev) 204 if (llc->dev)
198 dev_put(llc->dev); 205 dev_put(llc->dev);
@@ -252,7 +259,14 @@ static int llc_ui_autobind(struct socket *sock, struct sockaddr_llc *addr)
252 if (!sock_flag(sk, SOCK_ZAPPED)) 259 if (!sock_flag(sk, SOCK_ZAPPED))
253 goto out; 260 goto out;
254 rc = -ENODEV; 261 rc = -ENODEV;
255 llc->dev = dev_getfirstbyhwtype(&init_net, addr->sllc_arphrd); 262 if (sk->sk_bound_dev_if) {
263 llc->dev = dev_get_by_index(&init_net, sk->sk_bound_dev_if);
264 if (llc->dev && addr->sllc_arphrd != llc->dev->type) {
265 dev_put(llc->dev);
266 llc->dev = NULL;
267 }
268 } else
269 llc->dev = dev_getfirstbyhwtype(&init_net, addr->sllc_arphrd);
256 if (!llc->dev) 270 if (!llc->dev)
257 goto out; 271 goto out;
258 rc = -EUSERS; 272 rc = -EUSERS;
@@ -303,7 +317,25 @@ static int llc_ui_bind(struct socket *sock, struct sockaddr *uaddr, int addrlen)
303 goto out; 317 goto out;
304 rc = -ENODEV; 318 rc = -ENODEV;
305 rtnl_lock(); 319 rtnl_lock();
306 llc->dev = dev_getbyhwaddr(&init_net, addr->sllc_arphrd, addr->sllc_mac); 320 if (sk->sk_bound_dev_if) {
321 llc->dev = dev_get_by_index(&init_net, sk->sk_bound_dev_if);
322 if (llc->dev) {
323 if (!addr->sllc_arphrd)
324 addr->sllc_arphrd = llc->dev->type;
325 if (llc_mac_null(addr->sllc_mac))
326 memcpy(addr->sllc_mac, llc->dev->dev_addr,
327 IFHWADDRLEN);
328 if (addr->sllc_arphrd != llc->dev->type ||
329 !llc_mac_match(addr->sllc_mac,
330 llc->dev->dev_addr)) {
331 rc = -EINVAL;
332 dev_put(llc->dev);
333 llc->dev = NULL;
334 }
335 }
336 } else
337 llc->dev = dev_getbyhwaddr(&init_net, addr->sllc_arphrd,
338 addr->sllc_mac);
307 rtnl_unlock(); 339 rtnl_unlock();
308 if (!llc->dev) 340 if (!llc->dev)
309 goto out; 341 goto out;
@@ -319,7 +351,6 @@ static int llc_ui_bind(struct socket *sock, struct sockaddr *uaddr, int addrlen)
319 rc = -EBUSY; /* some other network layer is using the sap */ 351 rc = -EBUSY; /* some other network layer is using the sap */
320 if (!sap) 352 if (!sap)
321 goto out; 353 goto out;
322 llc_sap_hold(sap);
323 } else { 354 } else {
324 struct llc_addr laddr, daddr; 355 struct llc_addr laddr, daddr;
325 struct sock *ask; 356 struct sock *ask;
@@ -588,6 +619,20 @@ static int llc_wait_data(struct sock *sk, long timeo)
588 return rc; 619 return rc;
589} 620}
590 621
622static void llc_cmsg_rcv(struct msghdr *msg, struct sk_buff *skb)
623{
624 struct llc_sock *llc = llc_sk(skb->sk);
625
626 if (llc->cmsg_flags & LLC_CMSG_PKTINFO) {
627 struct llc_pktinfo info;
628
629 info.lpi_ifindex = llc_sk(skb->sk)->dev->ifindex;
630 llc_pdu_decode_dsap(skb, &info.lpi_sap);
631 llc_pdu_decode_da(skb, info.lpi_mac);
632 put_cmsg(msg, SOL_LLC, LLC_OPT_PKTINFO, sizeof(info), &info);
633 }
634}
635
591/** 636/**
592 * llc_ui_accept - accept a new incoming connection. 637 * llc_ui_accept - accept a new incoming connection.
593 * @sock: Socket which connections arrive on. 638 * @sock: Socket which connections arrive on.
@@ -809,6 +854,8 @@ copy_uaddr:
809 memcpy(uaddr, llc_ui_skb_cb(skb), sizeof(*uaddr)); 854 memcpy(uaddr, llc_ui_skb_cb(skb), sizeof(*uaddr));
810 msg->msg_namelen = sizeof(*uaddr); 855 msg->msg_namelen = sizeof(*uaddr);
811 } 856 }
857 if (llc_sk(sk)->cmsg_flags)
858 llc_cmsg_rcv(msg, skb);
812 goto out; 859 goto out;
813} 860}
814 861
@@ -1027,6 +1074,12 @@ static int llc_ui_setsockopt(struct socket *sock, int level, int optname,
1027 goto out; 1074 goto out;
1028 llc->rw = opt; 1075 llc->rw = opt;
1029 break; 1076 break;
1077 case LLC_OPT_PKTINFO:
1078 if (opt)
1079 llc->cmsg_flags |= LLC_CMSG_PKTINFO;
1080 else
1081 llc->cmsg_flags &= ~LLC_CMSG_PKTINFO;
1082 break;
1030 default: 1083 default:
1031 rc = -ENOPROTOOPT; 1084 rc = -ENOPROTOOPT;
1032 goto out; 1085 goto out;
@@ -1080,6 +1133,9 @@ static int llc_ui_getsockopt(struct socket *sock, int level, int optname,
1080 val = llc->k; break; 1133 val = llc->k; break;
1081 case LLC_OPT_RX_WIN: 1134 case LLC_OPT_RX_WIN:
1082 val = llc->rw; break; 1135 val = llc->rw; break;
1136 case LLC_OPT_PKTINFO:
1137 val = (llc->cmsg_flags & LLC_CMSG_PKTINFO) != 0;
1138 break;
1083 default: 1139 default:
1084 rc = -ENOPROTOOPT; 1140 rc = -ENOPROTOOPT;
1085 goto out; 1141 goto out;
@@ -1092,7 +1148,7 @@ out:
1092 return rc; 1148 return rc;
1093} 1149}
1094 1150
1095static struct net_proto_family llc_ui_family_ops = { 1151static const struct net_proto_family llc_ui_family_ops = {
1096 .family = PF_LLC, 1152 .family = PF_LLC,
1097 .create = llc_ui_create, 1153 .create = llc_ui_create,
1098 .owner = THIS_MODULE, 1154 .owner = THIS_MODULE,
diff --git a/net/llc/llc_c_ac.c b/net/llc/llc_c_ac.c
index 019c780512e8..ea225bd2672c 100644
--- a/net/llc/llc_c_ac.c
+++ b/net/llc/llc_c_ac.c
@@ -18,6 +18,7 @@
18 * See the GNU General Public License for more details. 18 * See the GNU General Public License for more details.
19 */ 19 */
20#include <linux/netdevice.h> 20#include <linux/netdevice.h>
21#include <linux/slab.h>
21#include <net/llc_conn.h> 22#include <net/llc_conn.h>
22#include <net/llc_sap.h> 23#include <net/llc_sap.h>
23#include <net/sock.h> 24#include <net/sock.h>
@@ -1437,7 +1438,7 @@ static void llc_process_tmr_ev(struct sock *sk, struct sk_buff *skb)
1437 llc_conn_state_process(sk, skb); 1438 llc_conn_state_process(sk, skb);
1438 else { 1439 else {
1439 llc_set_backlog_type(skb, LLC_EVENT); 1440 llc_set_backlog_type(skb, LLC_EVENT);
1440 sk_add_backlog(sk, skb); 1441 __sk_add_backlog(sk, skb);
1441 } 1442 }
1442 } 1443 }
1443} 1444}
diff --git a/net/llc/llc_conn.c b/net/llc/llc_conn.c
index c6bab39b018e..ba137a6a224d 100644
--- a/net/llc/llc_conn.c
+++ b/net/llc/llc_conn.c
@@ -13,6 +13,7 @@
13 */ 13 */
14 14
15#include <linux/init.h> 15#include <linux/init.h>
16#include <linux/slab.h>
16#include <net/llc_sap.h> 17#include <net/llc_sap.h>
17#include <net/llc_conn.h> 18#include <net/llc_conn.h>
18#include <net/sock.h> 19#include <net/sock.h>
@@ -468,6 +469,19 @@ static int llc_exec_conn_trans_actions(struct sock *sk,
468 return rc; 469 return rc;
469} 470}
470 471
472static inline bool llc_estab_match(const struct llc_sap *sap,
473 const struct llc_addr *daddr,
474 const struct llc_addr *laddr,
475 const struct sock *sk)
476{
477 struct llc_sock *llc = llc_sk(sk);
478
479 return llc->laddr.lsap == laddr->lsap &&
480 llc->daddr.lsap == daddr->lsap &&
481 llc_mac_match(llc->laddr.mac, laddr->mac) &&
482 llc_mac_match(llc->daddr.mac, daddr->mac);
483}
484
471/** 485/**
472 * __llc_lookup_established - Finds connection for the remote/local sap/mac 486 * __llc_lookup_established - Finds connection for the remote/local sap/mac
473 * @sap: SAP 487 * @sap: SAP
@@ -484,23 +498,35 @@ static struct sock *__llc_lookup_established(struct llc_sap *sap,
484 struct llc_addr *laddr) 498 struct llc_addr *laddr)
485{ 499{
486 struct sock *rc; 500 struct sock *rc;
487 struct hlist_node *node; 501 struct hlist_nulls_node *node;
488 502 int slot = llc_sk_laddr_hashfn(sap, laddr);
489 read_lock(&sap->sk_list.lock); 503 struct hlist_nulls_head *laddr_hb = &sap->sk_laddr_hash[slot];
490 sk_for_each(rc, node, &sap->sk_list.list) { 504
491 struct llc_sock *llc = llc_sk(rc); 505 rcu_read_lock();
492 506again:
493 if (llc->laddr.lsap == laddr->lsap && 507 sk_nulls_for_each_rcu(rc, node, laddr_hb) {
494 llc->daddr.lsap == daddr->lsap && 508 if (llc_estab_match(sap, daddr, laddr, rc)) {
495 llc_mac_match(llc->laddr.mac, laddr->mac) && 509 /* Extra checks required by SLAB_DESTROY_BY_RCU */
496 llc_mac_match(llc->daddr.mac, daddr->mac)) { 510 if (unlikely(!atomic_inc_not_zero(&rc->sk_refcnt)))
497 sock_hold(rc); 511 goto again;
512 if (unlikely(llc_sk(rc)->sap != sap ||
513 !llc_estab_match(sap, daddr, laddr, rc))) {
514 sock_put(rc);
515 continue;
516 }
498 goto found; 517 goto found;
499 } 518 }
500 } 519 }
501 rc = NULL; 520 rc = NULL;
521 /*
522 * if the nulls value we got at the end of this lookup is
523 * not the expected one, we must restart lookup.
524 * We probably met an item that was moved to another chain.
525 */
526 if (unlikely(get_nulls_value(node) != slot))
527 goto again;
502found: 528found:
503 read_unlock(&sap->sk_list.lock); 529 rcu_read_unlock();
504 return rc; 530 return rc;
505} 531}
506 532
@@ -516,6 +542,53 @@ struct sock *llc_lookup_established(struct llc_sap *sap,
516 return sk; 542 return sk;
517} 543}
518 544
545static inline bool llc_listener_match(const struct llc_sap *sap,
546 const struct llc_addr *laddr,
547 const struct sock *sk)
548{
549 struct llc_sock *llc = llc_sk(sk);
550
551 return sk->sk_type == SOCK_STREAM && sk->sk_state == TCP_LISTEN &&
552 llc->laddr.lsap == laddr->lsap &&
553 llc_mac_match(llc->laddr.mac, laddr->mac);
554}
555
556static struct sock *__llc_lookup_listener(struct llc_sap *sap,
557 struct llc_addr *laddr)
558{
559 struct sock *rc;
560 struct hlist_nulls_node *node;
561 int slot = llc_sk_laddr_hashfn(sap, laddr);
562 struct hlist_nulls_head *laddr_hb = &sap->sk_laddr_hash[slot];
563
564 rcu_read_lock();
565again:
566 sk_nulls_for_each_rcu(rc, node, laddr_hb) {
567 if (llc_listener_match(sap, laddr, rc)) {
568 /* Extra checks required by SLAB_DESTROY_BY_RCU */
569 if (unlikely(!atomic_inc_not_zero(&rc->sk_refcnt)))
570 goto again;
571 if (unlikely(llc_sk(rc)->sap != sap ||
572 !llc_listener_match(sap, laddr, rc))) {
573 sock_put(rc);
574 continue;
575 }
576 goto found;
577 }
578 }
579 rc = NULL;
580 /*
581 * if the nulls value we got at the end of this lookup is
582 * not the expected one, we must restart lookup.
583 * We probably met an item that was moved to another chain.
584 */
585 if (unlikely(get_nulls_value(node) != slot))
586 goto again;
587found:
588 rcu_read_unlock();
589 return rc;
590}
591
519/** 592/**
520 * llc_lookup_listener - Finds listener for local MAC + SAP 593 * llc_lookup_listener - Finds listener for local MAC + SAP
521 * @sap: SAP 594 * @sap: SAP
@@ -529,24 +602,12 @@ struct sock *llc_lookup_established(struct llc_sap *sap,
529static struct sock *llc_lookup_listener(struct llc_sap *sap, 602static struct sock *llc_lookup_listener(struct llc_sap *sap,
530 struct llc_addr *laddr) 603 struct llc_addr *laddr)
531{ 604{
532 struct sock *rc; 605 static struct llc_addr null_addr;
533 struct hlist_node *node; 606 struct sock *rc = __llc_lookup_listener(sap, laddr);
534 607
535 read_lock(&sap->sk_list.lock); 608 if (!rc)
536 sk_for_each(rc, node, &sap->sk_list.list) { 609 rc = __llc_lookup_listener(sap, &null_addr);
537 struct llc_sock *llc = llc_sk(rc);
538 610
539 if (rc->sk_type == SOCK_STREAM && rc->sk_state == TCP_LISTEN &&
540 llc->laddr.lsap == laddr->lsap &&
541 (llc_mac_match(llc->laddr.mac, laddr->mac) ||
542 llc_mac_null(llc->laddr.mac))) {
543 sock_hold(rc);
544 goto found;
545 }
546 }
547 rc = NULL;
548found:
549 read_unlock(&sap->sk_list.lock);
550 return rc; 611 return rc;
551} 612}
552 613
@@ -647,15 +708,22 @@ static int llc_find_offset(int state, int ev_type)
647 * @sap: SAP 708 * @sap: SAP
648 * @sk: socket 709 * @sk: socket
649 * 710 *
650 * This function adds a socket to sk_list of a SAP. 711 * This function adds a socket to the hash tables of a SAP.
651 */ 712 */
652void llc_sap_add_socket(struct llc_sap *sap, struct sock *sk) 713void llc_sap_add_socket(struct llc_sap *sap, struct sock *sk)
653{ 714{
715 struct llc_sock *llc = llc_sk(sk);
716 struct hlist_head *dev_hb = llc_sk_dev_hash(sap, llc->dev->ifindex);
717 struct hlist_nulls_head *laddr_hb = llc_sk_laddr_hash(sap, &llc->laddr);
718
654 llc_sap_hold(sap); 719 llc_sap_hold(sap);
655 write_lock_bh(&sap->sk_list.lock);
656 llc_sk(sk)->sap = sap; 720 llc_sk(sk)->sap = sap;
657 sk_add_node(sk, &sap->sk_list.list); 721
658 write_unlock_bh(&sap->sk_list.lock); 722 spin_lock_bh(&sap->sk_lock);
723 sap->sk_count++;
724 sk_nulls_add_node_rcu(sk, laddr_hb);
725 hlist_add_head(&llc->dev_hash_node, dev_hb);
726 spin_unlock_bh(&sap->sk_lock);
659} 727}
660 728
661/** 729/**
@@ -663,14 +731,18 @@ void llc_sap_add_socket(struct llc_sap *sap, struct sock *sk)
663 * @sap: SAP 731 * @sap: SAP
664 * @sk: socket 732 * @sk: socket
665 * 733 *
666 * This function removes a connection from sk_list.list of a SAP if 734 * This function removes a connection from the hash tables of a SAP if
667 * the connection was in this list. 735 * the connection was in this list.
668 */ 736 */
669void llc_sap_remove_socket(struct llc_sap *sap, struct sock *sk) 737void llc_sap_remove_socket(struct llc_sap *sap, struct sock *sk)
670{ 738{
671 write_lock_bh(&sap->sk_list.lock); 739 struct llc_sock *llc = llc_sk(sk);
672 sk_del_node_init(sk); 740
673 write_unlock_bh(&sap->sk_list.lock); 741 spin_lock_bh(&sap->sk_lock);
742 sk_nulls_del_node_init_rcu(sk);
743 hlist_del(&llc->dev_hash_node);
744 sap->sk_count--;
745 spin_unlock_bh(&sap->sk_lock);
674 llc_sap_put(sap); 746 llc_sap_put(sap);
675} 747}
676 748
@@ -756,7 +828,8 @@ void llc_conn_handler(struct llc_sap *sap, struct sk_buff *skb)
756 else { 828 else {
757 dprintk("%s: adding to backlog...\n", __func__); 829 dprintk("%s: adding to backlog...\n", __func__);
758 llc_set_backlog_type(skb, LLC_PACKET); 830 llc_set_backlog_type(skb, LLC_PACKET);
759 sk_add_backlog(sk, skb); 831 if (sk_add_backlog(sk, skb))
832 goto drop_unlock;
760 } 833 }
761out: 834out:
762 bh_unlock_sock(sk); 835 bh_unlock_sock(sk);
diff --git a/net/llc/llc_core.c b/net/llc/llc_core.c
index ff4c0ab96a69..78167e81dfeb 100644
--- a/net/llc/llc_core.c
+++ b/net/llc/llc_core.c
@@ -23,7 +23,7 @@
23#include <net/llc.h> 23#include <net/llc.h>
24 24
25LIST_HEAD(llc_sap_list); 25LIST_HEAD(llc_sap_list);
26DEFINE_RWLOCK(llc_sap_list_lock); 26DEFINE_SPINLOCK(llc_sap_list_lock);
27 27
28/** 28/**
29 * llc_sap_alloc - allocates and initializes sap. 29 * llc_sap_alloc - allocates and initializes sap.
@@ -33,40 +33,19 @@ DEFINE_RWLOCK(llc_sap_list_lock);
33static struct llc_sap *llc_sap_alloc(void) 33static struct llc_sap *llc_sap_alloc(void)
34{ 34{
35 struct llc_sap *sap = kzalloc(sizeof(*sap), GFP_ATOMIC); 35 struct llc_sap *sap = kzalloc(sizeof(*sap), GFP_ATOMIC);
36 int i;
36 37
37 if (sap) { 38 if (sap) {
38 /* sap->laddr.mac - leave as a null, it's filled by bind */ 39 /* sap->laddr.mac - leave as a null, it's filled by bind */
39 sap->state = LLC_SAP_STATE_ACTIVE; 40 sap->state = LLC_SAP_STATE_ACTIVE;
40 rwlock_init(&sap->sk_list.lock); 41 spin_lock_init(&sap->sk_lock);
42 for (i = 0; i < LLC_SK_LADDR_HASH_ENTRIES; i++)
43 INIT_HLIST_NULLS_HEAD(&sap->sk_laddr_hash[i], i);
41 atomic_set(&sap->refcnt, 1); 44 atomic_set(&sap->refcnt, 1);
42 } 45 }
43 return sap; 46 return sap;
44} 47}
45 48
46/**
47 * llc_add_sap - add sap to station list
48 * @sap: Address of the sap
49 *
50 * Adds a sap to the LLC's station sap list.
51 */
52static void llc_add_sap(struct llc_sap *sap)
53{
54 list_add_tail(&sap->node, &llc_sap_list);
55}
56
57/**
58 * llc_del_sap - del sap from station list
59 * @sap: Address of the sap
60 *
61 * Removes a sap to the LLC's station sap list.
62 */
63static void llc_del_sap(struct llc_sap *sap)
64{
65 write_lock_bh(&llc_sap_list_lock);
66 list_del(&sap->node);
67 write_unlock_bh(&llc_sap_list_lock);
68}
69
70static struct llc_sap *__llc_sap_find(unsigned char sap_value) 49static struct llc_sap *__llc_sap_find(unsigned char sap_value)
71{ 50{
72 struct llc_sap* sap; 51 struct llc_sap* sap;
@@ -90,13 +69,13 @@ out:
90 */ 69 */
91struct llc_sap *llc_sap_find(unsigned char sap_value) 70struct llc_sap *llc_sap_find(unsigned char sap_value)
92{ 71{
93 struct llc_sap* sap; 72 struct llc_sap *sap;
94 73
95 read_lock_bh(&llc_sap_list_lock); 74 rcu_read_lock_bh();
96 sap = __llc_sap_find(sap_value); 75 sap = __llc_sap_find(sap_value);
97 if (sap) 76 if (sap)
98 llc_sap_hold(sap); 77 llc_sap_hold(sap);
99 read_unlock_bh(&llc_sap_list_lock); 78 rcu_read_unlock_bh();
100 return sap; 79 return sap;
101} 80}
102 81
@@ -117,7 +96,7 @@ struct llc_sap *llc_sap_open(unsigned char lsap,
117{ 96{
118 struct llc_sap *sap = NULL; 97 struct llc_sap *sap = NULL;
119 98
120 write_lock_bh(&llc_sap_list_lock); 99 spin_lock_bh(&llc_sap_list_lock);
121 if (__llc_sap_find(lsap)) /* SAP already exists */ 100 if (__llc_sap_find(lsap)) /* SAP already exists */
122 goto out; 101 goto out;
123 sap = llc_sap_alloc(); 102 sap = llc_sap_alloc();
@@ -125,9 +104,9 @@ struct llc_sap *llc_sap_open(unsigned char lsap,
125 goto out; 104 goto out;
126 sap->laddr.lsap = lsap; 105 sap->laddr.lsap = lsap;
127 sap->rcv_func = func; 106 sap->rcv_func = func;
128 llc_add_sap(sap); 107 list_add_tail_rcu(&sap->node, &llc_sap_list);
129out: 108out:
130 write_unlock_bh(&llc_sap_list_lock); 109 spin_unlock_bh(&llc_sap_list_lock);
131 return sap; 110 return sap;
132} 111}
133 112
@@ -142,8 +121,14 @@ out:
142 */ 121 */
143void llc_sap_close(struct llc_sap *sap) 122void llc_sap_close(struct llc_sap *sap)
144{ 123{
145 WARN_ON(!hlist_empty(&sap->sk_list.list)); 124 WARN_ON(sap->sk_count);
146 llc_del_sap(sap); 125
126 spin_lock_bh(&llc_sap_list_lock);
127 list_del_rcu(&sap->node);
128 spin_unlock_bh(&llc_sap_list_lock);
129
130 synchronize_rcu();
131
147 kfree(sap); 132 kfree(sap);
148} 133}
149 134
diff --git a/net/llc/llc_if.c b/net/llc/llc_if.c
index a89917130a7b..25c31c0a3fdb 100644
--- a/net/llc/llc_if.c
+++ b/net/llc/llc_if.c
@@ -11,6 +11,7 @@
11 * 11 *
12 * See the GNU General Public License for more details. 12 * See the GNU General Public License for more details.
13 */ 13 */
14#include <linux/gfp.h>
14#include <linux/module.h> 15#include <linux/module.h>
15#include <linux/kernel.h> 16#include <linux/kernel.h>
16#include <linux/netdevice.h> 17#include <linux/netdevice.h>
diff --git a/net/llc/llc_input.c b/net/llc/llc_input.c
index 57ad974e4d94..f99687439139 100644
--- a/net/llc/llc_input.c
+++ b/net/llc/llc_input.c
@@ -12,6 +12,7 @@
12 * See the GNU General Public License for more details. 12 * See the GNU General Public License for more details.
13 */ 13 */
14#include <linux/netdevice.h> 14#include <linux/netdevice.h>
15#include <linux/slab.h>
15#include <net/net_namespace.h> 16#include <net/net_namespace.h>
16#include <net/llc.h> 17#include <net/llc.h>
17#include <net/llc_pdu.h> 18#include <net/llc_pdu.h>
diff --git a/net/llc/llc_output.c b/net/llc/llc_output.c
index 754f4fedc852..b38a1079a98e 100644
--- a/net/llc/llc_output.c
+++ b/net/llc/llc_output.c
@@ -33,48 +33,19 @@
33int llc_mac_hdr_init(struct sk_buff *skb, 33int llc_mac_hdr_init(struct sk_buff *skb,
34 const unsigned char *sa, const unsigned char *da) 34 const unsigned char *sa, const unsigned char *da)
35{ 35{
36 int rc = 0; 36 int rc = -EINVAL;
37 37
38 switch (skb->dev->type) { 38 switch (skb->dev->type) {
39#ifdef CONFIG_TR 39 case ARPHRD_IEEE802_TR:
40 case ARPHRD_IEEE802_TR: {
41 struct net_device *dev = skb->dev;
42 struct trh_hdr *trh;
43
44 skb_push(skb, sizeof(*trh));
45 skb_reset_mac_header(skb);
46 trh = tr_hdr(skb);
47 trh->ac = AC;
48 trh->fc = LLC_FRAME;
49 if (sa)
50 memcpy(trh->saddr, sa, dev->addr_len);
51 else
52 memset(trh->saddr, 0, dev->addr_len);
53 if (da) {
54 memcpy(trh->daddr, da, dev->addr_len);
55 tr_source_route(skb, trh, dev);
56 skb_reset_mac_header(skb);
57 }
58 break;
59 }
60#endif
61 case ARPHRD_ETHER: 40 case ARPHRD_ETHER:
62 case ARPHRD_LOOPBACK: { 41 case ARPHRD_LOOPBACK:
63 unsigned short len = skb->len; 42 rc = dev_hard_header(skb, skb->dev, ETH_P_802_2, da, sa,
64 struct ethhdr *eth; 43 skb->len);
65 44 if (rc > 0)
66 skb_push(skb, sizeof(*eth)); 45 rc = 0;
67 skb_reset_mac_header(skb);
68 eth = eth_hdr(skb);
69 eth->h_proto = htons(len);
70 memcpy(eth->h_dest, da, ETH_ALEN);
71 memcpy(eth->h_source, sa, ETH_ALEN);
72 break; 46 break;
73 }
74 default: 47 default:
75 printk(KERN_WARNING "device type not supported: %d\n", 48 WARN(1, "device type not supported: %d\n", skb->dev->type);
76 skb->dev->type);
77 rc = -EINVAL;
78 } 49 }
79 return rc; 50 return rc;
80} 51}
diff --git a/net/llc/llc_proc.c b/net/llc/llc_proc.c
index be47ac427f6b..7af1ff2d1f19 100644
--- a/net/llc/llc_proc.c
+++ b/net/llc/llc_proc.c
@@ -32,21 +32,23 @@ static void llc_ui_format_mac(struct seq_file *seq, u8 *addr)
32 32
33static struct sock *llc_get_sk_idx(loff_t pos) 33static struct sock *llc_get_sk_idx(loff_t pos)
34{ 34{
35 struct list_head *sap_entry;
36 struct llc_sap *sap; 35 struct llc_sap *sap;
37 struct hlist_node *node;
38 struct sock *sk = NULL; 36 struct sock *sk = NULL;
39 37 int i;
40 list_for_each(sap_entry, &llc_sap_list) { 38
41 sap = list_entry(sap_entry, struct llc_sap, node); 39 list_for_each_entry_rcu(sap, &llc_sap_list, node) {
42 40 spin_lock_bh(&sap->sk_lock);
43 read_lock_bh(&sap->sk_list.lock); 41 for (i = 0; i < LLC_SK_LADDR_HASH_ENTRIES; i++) {
44 sk_for_each(sk, node, &sap->sk_list.list) { 42 struct hlist_nulls_head *head = &sap->sk_laddr_hash[i];
45 if (!pos) 43 struct hlist_nulls_node *node;
46 goto found; 44
47 --pos; 45 sk_nulls_for_each(sk, node, head) {
46 if (!pos)
47 goto found; /* keep the lock */
48 --pos;
49 }
48 } 50 }
49 read_unlock_bh(&sap->sk_list.lock); 51 spin_unlock_bh(&sap->sk_lock);
50 } 52 }
51 sk = NULL; 53 sk = NULL;
52found: 54found:
@@ -57,10 +59,23 @@ static void *llc_seq_start(struct seq_file *seq, loff_t *pos)
57{ 59{
58 loff_t l = *pos; 60 loff_t l = *pos;
59 61
60 read_lock_bh(&llc_sap_list_lock); 62 rcu_read_lock_bh();
61 return l ? llc_get_sk_idx(--l) : SEQ_START_TOKEN; 63 return l ? llc_get_sk_idx(--l) : SEQ_START_TOKEN;
62} 64}
63 65
66static struct sock *laddr_hash_next(struct llc_sap *sap, int bucket)
67{
68 struct hlist_nulls_node *node;
69 struct sock *sk = NULL;
70
71 while (++bucket < LLC_SK_LADDR_HASH_ENTRIES)
72 sk_nulls_for_each(sk, node, &sap->sk_laddr_hash[bucket])
73 goto out;
74
75out:
76 return sk;
77}
78
64static void *llc_seq_next(struct seq_file *seq, void *v, loff_t *pos) 79static void *llc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
65{ 80{
66 struct sock* sk, *next; 81 struct sock* sk, *next;
@@ -73,25 +88,23 @@ static void *llc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
73 goto out; 88 goto out;
74 } 89 }
75 sk = v; 90 sk = v;
76 next = sk_next(sk); 91 next = sk_nulls_next(sk);
77 if (next) { 92 if (next) {
78 sk = next; 93 sk = next;
79 goto out; 94 goto out;
80 } 95 }
81 llc = llc_sk(sk); 96 llc = llc_sk(sk);
82 sap = llc->sap; 97 sap = llc->sap;
83 read_unlock_bh(&sap->sk_list.lock); 98 sk = laddr_hash_next(sap, llc_sk_laddr_hashfn(sap, &llc->laddr));
84 sk = NULL; 99 if (sk)
85 for (;;) { 100 goto out;
86 if (sap->node.next == &llc_sap_list) 101 spin_unlock_bh(&sap->sk_lock);
87 break; 102 list_for_each_entry_continue_rcu(sap, &llc_sap_list, node) {
88 sap = list_entry(sap->node.next, struct llc_sap, node); 103 spin_lock_bh(&sap->sk_lock);
89 read_lock_bh(&sap->sk_list.lock); 104 sk = laddr_hash_next(sap, -1);
90 if (!hlist_empty(&sap->sk_list.list)) { 105 if (sk)
91 sk = sk_head(&sap->sk_list.list); 106 break; /* keep the lock */
92 break; 107 spin_unlock_bh(&sap->sk_lock);
93 }
94 read_unlock_bh(&sap->sk_list.lock);
95 } 108 }
96out: 109out:
97 return sk; 110 return sk;
@@ -104,9 +117,9 @@ static void llc_seq_stop(struct seq_file *seq, void *v)
104 struct llc_sock *llc = llc_sk(sk); 117 struct llc_sock *llc = llc_sk(sk);
105 struct llc_sap *sap = llc->sap; 118 struct llc_sap *sap = llc->sap;
106 119
107 read_unlock_bh(&sap->sk_list.lock); 120 spin_unlock_bh(&sap->sk_lock);
108 } 121 }
109 read_unlock_bh(&llc_sap_list_lock); 122 rcu_read_unlock_bh();
110} 123}
111 124
112static int llc_seq_socket_show(struct seq_file *seq, void *v) 125static int llc_seq_socket_show(struct seq_file *seq, void *v)
diff --git a/net/llc/llc_sap.c b/net/llc/llc_sap.c
index 008de1fc42ca..94e7fca75b85 100644
--- a/net/llc/llc_sap.c
+++ b/net/llc/llc_sap.c
@@ -23,6 +23,7 @@
23#include <net/sock.h> 23#include <net/sock.h>
24#include <net/tcp_states.h> 24#include <net/tcp_states.h>
25#include <linux/llc.h> 25#include <linux/llc.h>
26#include <linux/slab.h>
26 27
27static int llc_mac_header_len(unsigned short devtype) 28static int llc_mac_header_len(unsigned short devtype)
28{ 29{
@@ -30,7 +31,7 @@ static int llc_mac_header_len(unsigned short devtype)
30 case ARPHRD_ETHER: 31 case ARPHRD_ETHER:
31 case ARPHRD_LOOPBACK: 32 case ARPHRD_LOOPBACK:
32 return sizeof(struct ethhdr); 33 return sizeof(struct ethhdr);
33#ifdef CONFIG_TR 34#if defined(CONFIG_TR) || defined(CONFIG_TR_MODULE)
34 case ARPHRD_IEEE802_TR: 35 case ARPHRD_IEEE802_TR:
35 return sizeof(struct trh_hdr); 36 return sizeof(struct trh_hdr);
36#endif 37#endif
@@ -297,6 +298,17 @@ static void llc_sap_rcv(struct llc_sap *sap, struct sk_buff *skb,
297 llc_sap_state_process(sap, skb); 298 llc_sap_state_process(sap, skb);
298} 299}
299 300
301static inline bool llc_dgram_match(const struct llc_sap *sap,
302 const struct llc_addr *laddr,
303 const struct sock *sk)
304{
305 struct llc_sock *llc = llc_sk(sk);
306
307 return sk->sk_type == SOCK_DGRAM &&
308 llc->laddr.lsap == laddr->lsap &&
309 llc_mac_match(llc->laddr.mac, laddr->mac);
310}
311
300/** 312/**
301 * llc_lookup_dgram - Finds dgram socket for the local sap/mac 313 * llc_lookup_dgram - Finds dgram socket for the local sap/mac
302 * @sap: SAP 314 * @sap: SAP
@@ -309,25 +321,68 @@ static struct sock *llc_lookup_dgram(struct llc_sap *sap,
309 const struct llc_addr *laddr) 321 const struct llc_addr *laddr)
310{ 322{
311 struct sock *rc; 323 struct sock *rc;
312 struct hlist_node *node; 324 struct hlist_nulls_node *node;
313 325 int slot = llc_sk_laddr_hashfn(sap, laddr);
314 read_lock_bh(&sap->sk_list.lock); 326 struct hlist_nulls_head *laddr_hb = &sap->sk_laddr_hash[slot];
315 sk_for_each(rc, node, &sap->sk_list.list) { 327
316 struct llc_sock *llc = llc_sk(rc); 328 rcu_read_lock_bh();
317 329again:
318 if (rc->sk_type == SOCK_DGRAM && 330 sk_nulls_for_each_rcu(rc, node, laddr_hb) {
319 llc->laddr.lsap == laddr->lsap && 331 if (llc_dgram_match(sap, laddr, rc)) {
320 llc_mac_match(llc->laddr.mac, laddr->mac)) { 332 /* Extra checks required by SLAB_DESTROY_BY_RCU */
321 sock_hold(rc); 333 if (unlikely(!atomic_inc_not_zero(&rc->sk_refcnt)))
334 goto again;
335 if (unlikely(llc_sk(rc)->sap != sap ||
336 !llc_dgram_match(sap, laddr, rc))) {
337 sock_put(rc);
338 continue;
339 }
322 goto found; 340 goto found;
323 } 341 }
324 } 342 }
325 rc = NULL; 343 rc = NULL;
344 /*
345 * if the nulls value we got at the end of this lookup is
346 * not the expected one, we must restart lookup.
347 * We probably met an item that was moved to another chain.
348 */
349 if (unlikely(get_nulls_value(node) != slot))
350 goto again;
326found: 351found:
327 read_unlock_bh(&sap->sk_list.lock); 352 rcu_read_unlock_bh();
328 return rc; 353 return rc;
329} 354}
330 355
356static inline bool llc_mcast_match(const struct llc_sap *sap,
357 const struct llc_addr *laddr,
358 const struct sk_buff *skb,
359 const struct sock *sk)
360{
361 struct llc_sock *llc = llc_sk(sk);
362
363 return sk->sk_type == SOCK_DGRAM &&
364 llc->laddr.lsap == laddr->lsap &&
365 llc->dev == skb->dev;
366}
367
368static void llc_do_mcast(struct llc_sap *sap, struct sk_buff *skb,
369 struct sock **stack, int count)
370{
371 struct sk_buff *skb1;
372 int i;
373
374 for (i = 0; i < count; i++) {
375 skb1 = skb_clone(skb, GFP_ATOMIC);
376 if (!skb1) {
377 sock_put(stack[i]);
378 continue;
379 }
380
381 llc_sap_rcv(sap, skb1, stack[i]);
382 sock_put(stack[i]);
383 }
384}
385
331/** 386/**
332 * llc_sap_mcast - Deliver multicast PDU's to all matching datagram sockets. 387 * llc_sap_mcast - Deliver multicast PDU's to all matching datagram sockets.
333 * @sap: SAP 388 * @sap: SAP
@@ -340,32 +395,31 @@ static void llc_sap_mcast(struct llc_sap *sap,
340 const struct llc_addr *laddr, 395 const struct llc_addr *laddr,
341 struct sk_buff *skb) 396 struct sk_buff *skb)
342{ 397{
343 struct sock *sk; 398 int i = 0, count = 256 / sizeof(struct sock *);
399 struct sock *sk, *stack[count];
344 struct hlist_node *node; 400 struct hlist_node *node;
401 struct llc_sock *llc;
402 struct hlist_head *dev_hb = llc_sk_dev_hash(sap, skb->dev->ifindex);
345 403
346 read_lock_bh(&sap->sk_list.lock); 404 spin_lock_bh(&sap->sk_lock);
347 sk_for_each(sk, node, &sap->sk_list.list) { 405 hlist_for_each_entry(llc, node, dev_hb, dev_hash_node) {
348 struct llc_sock *llc = llc_sk(sk);
349 struct sk_buff *skb1;
350 406
351 if (sk->sk_type != SOCK_DGRAM) 407 sk = &llc->sk;
352 continue;
353 408
354 if (llc->laddr.lsap != laddr->lsap) 409 if (!llc_mcast_match(sap, laddr, skb, sk))
355 continue; 410 continue;
356 411
357 if (llc->dev != skb->dev)
358 continue;
359
360 skb1 = skb_clone(skb, GFP_ATOMIC);
361 if (!skb1)
362 break;
363
364 sock_hold(sk); 412 sock_hold(sk);
365 llc_sap_rcv(sap, skb1, sk); 413 if (i < count)
366 sock_put(sk); 414 stack[i++] = sk;
415 else {
416 llc_do_mcast(sap, skb, stack, i);
417 i = 0;
418 }
367 } 419 }
368 read_unlock_bh(&sap->sk_list.lock); 420 spin_unlock_bh(&sap->sk_lock);
421
422 llc_do_mcast(sap, skb, stack, i);
369} 423}
370 424
371 425
diff --git a/net/llc/llc_station.c b/net/llc/llc_station.c
index 83da13339490..e4dae0244d76 100644
--- a/net/llc/llc_station.c
+++ b/net/llc/llc_station.c
@@ -13,6 +13,7 @@
13 */ 13 */
14#include <linux/init.h> 14#include <linux/init.h>
15#include <linux/module.h> 15#include <linux/module.h>
16#include <linux/slab.h>
16#include <net/llc.h> 17#include <net/llc.h>
17#include <net/llc_sap.h> 18#include <net/llc_sap.h>
18#include <net/llc_conn.h> 19#include <net/llc_conn.h>
diff --git a/net/llc/sysctl_net_llc.c b/net/llc/sysctl_net_llc.c
index 57b9304d444c..e2ebe3586263 100644
--- a/net/llc/sysctl_net_llc.c
+++ b/net/llc/sysctl_net_llc.c
@@ -15,86 +15,73 @@
15 15
16static struct ctl_table llc2_timeout_table[] = { 16static struct ctl_table llc2_timeout_table[] = {
17 { 17 {
18 .ctl_name = NET_LLC2_ACK_TIMEOUT,
19 .procname = "ack", 18 .procname = "ack",
20 .data = &sysctl_llc2_ack_timeout, 19 .data = &sysctl_llc2_ack_timeout,
21 .maxlen = sizeof(long), 20 .maxlen = sizeof(long),
22 .mode = 0644, 21 .mode = 0644,
23 .proc_handler = proc_dointvec_jiffies, 22 .proc_handler = proc_dointvec_jiffies,
24 .strategy = sysctl_jiffies,
25 }, 23 },
26 { 24 {
27 .ctl_name = NET_LLC2_BUSY_TIMEOUT,
28 .procname = "busy", 25 .procname = "busy",
29 .data = &sysctl_llc2_busy_timeout, 26 .data = &sysctl_llc2_busy_timeout,
30 .maxlen = sizeof(long), 27 .maxlen = sizeof(long),
31 .mode = 0644, 28 .mode = 0644,
32 .proc_handler = proc_dointvec_jiffies, 29 .proc_handler = proc_dointvec_jiffies,
33 .strategy = sysctl_jiffies,
34 }, 30 },
35 { 31 {
36 .ctl_name = NET_LLC2_P_TIMEOUT,
37 .procname = "p", 32 .procname = "p",
38 .data = &sysctl_llc2_p_timeout, 33 .data = &sysctl_llc2_p_timeout,
39 .maxlen = sizeof(long), 34 .maxlen = sizeof(long),
40 .mode = 0644, 35 .mode = 0644,
41 .proc_handler = proc_dointvec_jiffies, 36 .proc_handler = proc_dointvec_jiffies,
42 .strategy = sysctl_jiffies,
43 }, 37 },
44 { 38 {
45 .ctl_name = NET_LLC2_REJ_TIMEOUT,
46 .procname = "rej", 39 .procname = "rej",
47 .data = &sysctl_llc2_rej_timeout, 40 .data = &sysctl_llc2_rej_timeout,
48 .maxlen = sizeof(long), 41 .maxlen = sizeof(long),
49 .mode = 0644, 42 .mode = 0644,
50 .proc_handler = proc_dointvec_jiffies, 43 .proc_handler = proc_dointvec_jiffies,
51 .strategy = sysctl_jiffies,
52 }, 44 },
53 { 0 }, 45 { },
54}; 46};
55 47
56static struct ctl_table llc_station_table[] = { 48static struct ctl_table llc_station_table[] = {
57 { 49 {
58 .ctl_name = NET_LLC_STATION_ACK_TIMEOUT,
59 .procname = "ack_timeout", 50 .procname = "ack_timeout",
60 .data = &sysctl_llc_station_ack_timeout, 51 .data = &sysctl_llc_station_ack_timeout,
61 .maxlen = sizeof(long), 52 .maxlen = sizeof(long),
62 .mode = 0644, 53 .mode = 0644,
63 .proc_handler = proc_dointvec_jiffies, 54 .proc_handler = proc_dointvec_jiffies,
64 .strategy = sysctl_jiffies,
65 }, 55 },
66 { 0 }, 56 { },
67}; 57};
68 58
69static struct ctl_table llc2_dir_timeout_table[] = { 59static struct ctl_table llc2_dir_timeout_table[] = {
70 { 60 {
71 .ctl_name = NET_LLC2,
72 .procname = "timeout", 61 .procname = "timeout",
73 .mode = 0555, 62 .mode = 0555,
74 .child = llc2_timeout_table, 63 .child = llc2_timeout_table,
75 }, 64 },
76 { 0 }, 65 { },
77}; 66};
78 67
79static struct ctl_table llc_table[] = { 68static struct ctl_table llc_table[] = {
80 { 69 {
81 .ctl_name = NET_LLC2,
82 .procname = "llc2", 70 .procname = "llc2",
83 .mode = 0555, 71 .mode = 0555,
84 .child = llc2_dir_timeout_table, 72 .child = llc2_dir_timeout_table,
85 }, 73 },
86 { 74 {
87 .ctl_name = NET_LLC_STATION,
88 .procname = "station", 75 .procname = "station",
89 .mode = 0555, 76 .mode = 0555,
90 .child = llc_station_table, 77 .child = llc_station_table,
91 }, 78 },
92 { 0 }, 79 { },
93}; 80};
94 81
95static struct ctl_path llc_path[] = { 82static struct ctl_path llc_path[] = {
96 { .procname = "net", .ctl_name = CTL_NET, }, 83 { .procname = "net", },
97 { .procname = "llc", .ctl_name = NET_LLC, }, 84 { .procname = "llc", },
98 { } 85 { }
99}; 86};
100 87