Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/net/llc/llc_input.c
26278 views
1
/*
2
* llc_input.c - Minimal input path for LLC
3
*
4
* Copyright (c) 1997 by Procom Technology, Inc.
5
* 2001-2003 by Arnaldo Carvalho de Melo <[email protected]>
6
*
7
* This program can be redistributed or modified under the terms of the
8
* GNU General Public License as published by the Free Software Foundation.
9
* This program is distributed without any warranty or implied warranty
10
* of merchantability or fitness for a particular purpose.
11
*
12
* See the GNU General Public License for more details.
13
*/
14
#include <linux/netdevice.h>
15
#include <linux/slab.h>
16
#include <linux/export.h>
17
#include <net/net_namespace.h>
18
#include <net/llc.h>
19
#include <net/llc_pdu.h>
20
#include <net/llc_sap.h>
21
22
#if 0
23
#define dprintk(args...) printk(KERN_DEBUG args)
24
#else
25
#define dprintk(args...)
26
#endif
27
28
/*
29
* Packet handler for the station, registerable because in the minimal
30
* LLC core that is taking shape only the very minimal subset of LLC that
31
* is needed for things like IPX, Appletalk, etc will stay, with all the
32
* rest in the llc1 and llc2 modules.
33
*/
34
static void (*llc_station_handler)(struct sk_buff *skb);
35
36
/*
37
* Packet handlers for LLC_DEST_SAP and LLC_DEST_CONN.
38
*/
39
static void (*llc_type_handlers[2])(struct llc_sap *sap,
40
struct sk_buff *skb);
41
42
void llc_add_pack(int type, void (*handler)(struct llc_sap *sap,
43
struct sk_buff *skb))
44
{
45
smp_wmb(); /* ensure initialisation is complete before it's called */
46
if (type == LLC_DEST_SAP || type == LLC_DEST_CONN)
47
llc_type_handlers[type - 1] = handler;
48
}
49
50
void llc_remove_pack(int type)
51
{
52
if (type == LLC_DEST_SAP || type == LLC_DEST_CONN)
53
llc_type_handlers[type - 1] = NULL;
54
synchronize_net();
55
}
56
57
void llc_set_station_handler(void (*handler)(struct sk_buff *skb))
58
{
59
/* Ensure initialisation is complete before it's called */
60
if (handler)
61
smp_wmb();
62
63
llc_station_handler = handler;
64
65
if (!handler)
66
synchronize_net();
67
}
68
69
/**
70
* llc_pdu_type - returns which LLC component must handle for PDU
71
* @skb: input skb
72
*
73
* This function returns which LLC component must handle this PDU.
74
*/
75
static __inline__ int llc_pdu_type(struct sk_buff *skb)
76
{
77
int type = LLC_DEST_CONN; /* I-PDU or S-PDU type */
78
struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
79
80
if ((pdu->ctrl_1 & LLC_PDU_TYPE_MASK) != LLC_PDU_TYPE_U)
81
goto out;
82
switch (LLC_U_PDU_CMD(pdu)) {
83
case LLC_1_PDU_CMD_XID:
84
case LLC_1_PDU_CMD_UI:
85
case LLC_1_PDU_CMD_TEST:
86
type = LLC_DEST_SAP;
87
break;
88
case LLC_2_PDU_CMD_SABME:
89
case LLC_2_PDU_CMD_DISC:
90
case LLC_2_PDU_RSP_UA:
91
case LLC_2_PDU_RSP_DM:
92
case LLC_2_PDU_RSP_FRMR:
93
break;
94
default:
95
type = LLC_DEST_INVALID;
96
break;
97
}
98
out:
99
return type;
100
}
101
102
/**
103
* llc_fixup_skb - initializes skb pointers
104
* @skb: This argument points to incoming skb
105
*
106
* Initializes internal skb pointer to start of network layer by deriving
107
* length of LLC header; finds length of LLC control field in LLC header
108
* by looking at the two lowest-order bits of the first control field
109
* byte; field is either 3 or 4 bytes long.
110
*/
111
static inline int llc_fixup_skb(struct sk_buff *skb)
112
{
113
u8 llc_len = 2;
114
struct llc_pdu_un *pdu;
115
116
if (unlikely(!pskb_may_pull(skb, sizeof(*pdu))))
117
return 0;
118
119
pdu = (struct llc_pdu_un *)skb->data;
120
if ((pdu->ctrl_1 & LLC_PDU_TYPE_MASK) == LLC_PDU_TYPE_U)
121
llc_len = 1;
122
llc_len += 2;
123
124
if (unlikely(!pskb_may_pull(skb, llc_len)))
125
return 0;
126
127
skb_pull(skb, llc_len);
128
skb_reset_transport_header(skb);
129
if (skb->protocol == htons(ETH_P_802_2)) {
130
__be16 pdulen;
131
s32 data_size;
132
133
if (skb->mac_len < ETH_HLEN)
134
return 0;
135
136
pdulen = eth_hdr(skb)->h_proto;
137
data_size = ntohs(pdulen) - llc_len;
138
139
if (data_size < 0 ||
140
!pskb_may_pull(skb, data_size))
141
return 0;
142
if (unlikely(pskb_trim_rcsum(skb, data_size)))
143
return 0;
144
}
145
return 1;
146
}
147
148
/**
149
* llc_rcv - 802.2 entry point from net lower layers
150
* @skb: received pdu
151
* @dev: device that receive pdu
152
* @pt: packet type
153
* @orig_dev: the original receive net device
154
*
155
* When the system receives a 802.2 frame this function is called. It
156
* checks SAP and connection of received pdu and passes frame to
157
* llc_{station,sap,conn}_rcv for sending to proper state machine. If
158
* the frame is related to a busy connection (a connection is sending
159
* data now), it queues this frame in the connection's backlog.
160
*/
161
int llc_rcv(struct sk_buff *skb, struct net_device *dev,
162
struct packet_type *pt, struct net_device *orig_dev)
163
{
164
struct llc_sap *sap;
165
struct llc_pdu_sn *pdu;
166
int dest;
167
int (*rcv)(struct sk_buff *, struct net_device *,
168
struct packet_type *, struct net_device *);
169
void (*sta_handler)(struct sk_buff *skb);
170
void (*sap_handler)(struct llc_sap *sap, struct sk_buff *skb);
171
172
/*
173
* When the interface is in promisc. mode, drop all the crap that it
174
* receives, do not try to analyse it.
175
*/
176
if (unlikely(skb->pkt_type == PACKET_OTHERHOST)) {
177
dprintk("%s: PACKET_OTHERHOST\n", __func__);
178
goto drop;
179
}
180
skb = skb_share_check(skb, GFP_ATOMIC);
181
if (unlikely(!skb))
182
goto out;
183
if (unlikely(!llc_fixup_skb(skb)))
184
goto drop;
185
pdu = llc_pdu_sn_hdr(skb);
186
if (unlikely(!pdu->dsap)) /* NULL DSAP, refer to station */
187
goto handle_station;
188
sap = llc_sap_find(pdu->dsap);
189
if (unlikely(!sap)) {/* unknown SAP */
190
dprintk("%s: llc_sap_find(%02X) failed!\n", __func__,
191
pdu->dsap);
192
goto drop;
193
}
194
/*
195
* First the upper layer protocols that don't need the full
196
* LLC functionality
197
*/
198
rcv = rcu_dereference(sap->rcv_func);
199
dest = llc_pdu_type(skb);
200
sap_handler = dest ? READ_ONCE(llc_type_handlers[dest - 1]) : NULL;
201
if (unlikely(!sap_handler)) {
202
if (rcv)
203
rcv(skb, dev, pt, orig_dev);
204
else
205
kfree_skb(skb);
206
} else {
207
if (rcv) {
208
struct sk_buff *cskb = skb_clone(skb, GFP_ATOMIC);
209
if (cskb)
210
rcv(cskb, dev, pt, orig_dev);
211
}
212
sap_handler(sap, skb);
213
}
214
llc_sap_put(sap);
215
out:
216
return 0;
217
drop:
218
kfree_skb(skb);
219
goto out;
220
handle_station:
221
sta_handler = READ_ONCE(llc_station_handler);
222
if (!sta_handler)
223
goto drop;
224
sta_handler(skb);
225
goto out;
226
}
227
228
EXPORT_SYMBOL(llc_add_pack);
229
EXPORT_SYMBOL(llc_remove_pack);
230
EXPORT_SYMBOL(llc_set_station_handler);
231
232