Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * X.25 Packet Layer release 002 | |
3 | * | |
4 | * This is ALPHA test software. This code may break your machine, | |
5 | * randomly fail to work with new releases, misbehave and/or generally | |
6 | * screw up. It might even work. | |
7 | * | |
8 | * This code REQUIRES 2.1.15 or higher | |
9 | * | |
10 | * This module: | |
11 | * This module is free software; you can redistribute it and/or | |
12 | * modify it under the terms of the GNU General Public License | |
13 | * as published by the Free Software Foundation; either version | |
14 | * 2 of the License, or (at your option) any later version. | |
15 | * | |
16 | * History | |
17 | * X.25 001 Split from x25_subr.c | |
18 | * mar/20/00 Daniela Squassoni Disabling/enabling of facilities | |
19 | * negotiation. | |
ebc3f64b SP |
20 | * apr/14/05 Shaun Pereira - Allow fast select with no restriction |
21 | * on response. | |
1da177e4 LT |
22 | */ |
23 | ||
24 | #include <linux/kernel.h> | |
25 | #include <linux/string.h> | |
26 | #include <linux/skbuff.h> | |
27 | #include <net/sock.h> | |
28 | #include <net/x25.h> | |
29 | ||
30 | /* | |
31 | * Parse a set of facilities into the facilities structure. Unrecognised | |
32 | * facilities are written to the debug log file. | |
33 | */ | |
34 | int x25_parse_facilities(struct sk_buff *skb, | |
35 | struct x25_facilities *facilities, | |
36 | unsigned long *vc_fac_mask) | |
37 | { | |
38 | unsigned char *p = skb->data; | |
39 | unsigned int len = *p++; | |
40 | ||
41 | *vc_fac_mask = 0; | |
42 | ||
43 | while (len > 0) { | |
44 | switch (*p & X25_FAC_CLASS_MASK) { | |
45 | case X25_FAC_CLASS_A: | |
46 | switch (*p) { | |
47 | case X25_FAC_REVERSE: | |
ebc3f64b SP |
48 | if((p[1] & 0x81) == 0x81) { |
49 | facilities->reverse = p[1] & 0x81; | |
50 | *vc_fac_mask |= X25_MASK_REVERSE; | |
51 | break; | |
52 | } | |
53 | ||
54 | if((p[1] & 0x01) == 0x01) { | |
55 | facilities->reverse = p[1] & 0x01; | |
56 | *vc_fac_mask |= X25_MASK_REVERSE; | |
57 | break; | |
58 | } | |
59 | ||
60 | if((p[1] & 0x80) == 0x80) { | |
61 | facilities->reverse = p[1] & 0x80; | |
62 | *vc_fac_mask |= X25_MASK_REVERSE; | |
63 | break; | |
64 | } | |
65 | ||
66 | if(p[1] == 0x00) { | |
67 | facilities->reverse | |
68 | = X25_DEFAULT_REVERSE; | |
69 | *vc_fac_mask |= X25_MASK_REVERSE; | |
70 | break; | |
71 | } | |
72 | ||
1da177e4 LT |
73 | case X25_FAC_THROUGHPUT: |
74 | facilities->throughput = p[1]; | |
75 | *vc_fac_mask |= X25_MASK_THROUGHPUT; | |
76 | break; | |
77 | default: | |
78 | printk(KERN_DEBUG "X.25: unknown facility " | |
79 | "%02X, value %02X\n", | |
80 | p[0], p[1]); | |
81 | break; | |
82 | } | |
83 | p += 2; | |
84 | len -= 2; | |
85 | break; | |
86 | case X25_FAC_CLASS_B: | |
87 | switch (*p) { | |
88 | case X25_FAC_PACKET_SIZE: | |
89 | facilities->pacsize_in = p[1]; | |
90 | facilities->pacsize_out = p[2]; | |
91 | *vc_fac_mask |= X25_MASK_PACKET_SIZE; | |
92 | break; | |
93 | case X25_FAC_WINDOW_SIZE: | |
94 | facilities->winsize_in = p[1]; | |
95 | facilities->winsize_out = p[2]; | |
96 | *vc_fac_mask |= X25_MASK_WINDOW_SIZE; | |
97 | break; | |
98 | default: | |
99 | printk(KERN_DEBUG "X.25: unknown facility " | |
100 | "%02X, values %02X, %02X\n", | |
101 | p[0], p[1], p[2]); | |
102 | break; | |
103 | } | |
104 | p += 3; | |
105 | len -= 3; | |
106 | break; | |
107 | case X25_FAC_CLASS_C: | |
108 | printk(KERN_DEBUG "X.25: unknown facility %02X, " | |
109 | "values %02X, %02X, %02X\n", | |
110 | p[0], p[1], p[2], p[3]); | |
111 | p += 4; | |
112 | len -= 4; | |
113 | break; | |
114 | case X25_FAC_CLASS_D: | |
115 | printk(KERN_DEBUG "X.25: unknown facility %02X, " | |
116 | "length %d, values %02X, %02X, %02X, %02X\n", | |
117 | p[0], p[1], p[2], p[3], p[4], p[5]); | |
118 | len -= p[1] + 2; | |
119 | p += p[1] + 2; | |
120 | break; | |
121 | } | |
122 | } | |
123 | ||
124 | return p - skb->data; | |
125 | } | |
126 | ||
127 | /* | |
128 | * Create a set of facilities. | |
129 | */ | |
130 | int x25_create_facilities(unsigned char *buffer, | |
131 | struct x25_facilities *facilities, | |
132 | unsigned long facil_mask) | |
133 | { | |
134 | unsigned char *p = buffer + 1; | |
135 | int len; | |
136 | ||
137 | if (!facil_mask) { | |
138 | /* | |
139 | * Length of the facilities field in call_req or | |
140 | * call_accept packets | |
141 | */ | |
142 | buffer[0] = 0; | |
143 | len = 1; /* 1 byte for the length field */ | |
144 | return len; | |
145 | } | |
146 | ||
147 | if (facilities->reverse && (facil_mask & X25_MASK_REVERSE)) { | |
148 | *p++ = X25_FAC_REVERSE; | |
ebc3f64b | 149 | *p++ = facilities->reverse; |
1da177e4 LT |
150 | } |
151 | ||
152 | if (facilities->throughput && (facil_mask & X25_MASK_THROUGHPUT)) { | |
153 | *p++ = X25_FAC_THROUGHPUT; | |
154 | *p++ = facilities->throughput; | |
155 | } | |
156 | ||
157 | if ((facilities->pacsize_in || facilities->pacsize_out) && | |
158 | (facil_mask & X25_MASK_PACKET_SIZE)) { | |
159 | *p++ = X25_FAC_PACKET_SIZE; | |
160 | *p++ = facilities->pacsize_in ? : facilities->pacsize_out; | |
161 | *p++ = facilities->pacsize_out ? : facilities->pacsize_in; | |
162 | } | |
163 | ||
164 | if ((facilities->winsize_in || facilities->winsize_out) && | |
165 | (facil_mask & X25_MASK_WINDOW_SIZE)) { | |
166 | *p++ = X25_FAC_WINDOW_SIZE; | |
167 | *p++ = facilities->winsize_in ? : facilities->winsize_out; | |
168 | *p++ = facilities->winsize_out ? : facilities->winsize_in; | |
169 | } | |
170 | ||
171 | len = p - buffer; | |
172 | buffer[0] = len - 1; | |
173 | ||
174 | return len; | |
175 | } | |
176 | ||
177 | /* | |
178 | * Try to reach a compromise on a set of facilities. | |
179 | * | |
180 | * The only real problem is with reverse charging. | |
181 | */ | |
182 | int x25_negotiate_facilities(struct sk_buff *skb, struct sock *sk, | |
183 | struct x25_facilities *new) | |
184 | { | |
185 | struct x25_sock *x25 = x25_sk(sk); | |
186 | struct x25_facilities *ours = &x25->facilities; | |
187 | struct x25_facilities theirs; | |
188 | int len; | |
189 | ||
190 | memset(&theirs, 0, sizeof(theirs)); | |
191 | memcpy(new, ours, sizeof(*new)); | |
192 | ||
193 | len = x25_parse_facilities(skb, &theirs, &x25->vc_facil_mask); | |
194 | ||
195 | /* | |
196 | * They want reverse charging, we won't accept it. | |
197 | */ | |
ebc3f64b | 198 | if ((theirs.reverse & 0x01 ) && (ours->reverse & 0x01)) { |
1da177e4 LT |
199 | SOCK_DEBUG(sk, "X.25: rejecting reverse charging request"); |
200 | return -1; | |
201 | } | |
202 | ||
203 | new->reverse = theirs.reverse; | |
204 | ||
205 | if (theirs.throughput) { | |
206 | if (theirs.throughput < ours->throughput) { | |
207 | SOCK_DEBUG(sk, "X.25: throughput negotiated down"); | |
208 | new->throughput = theirs.throughput; | |
209 | } | |
210 | } | |
211 | ||
212 | if (theirs.pacsize_in && theirs.pacsize_out) { | |
213 | if (theirs.pacsize_in < ours->pacsize_in) { | |
214 | SOCK_DEBUG(sk, "X.25: packet size inwards negotiated down"); | |
215 | new->pacsize_in = theirs.pacsize_in; | |
216 | } | |
217 | if (theirs.pacsize_out < ours->pacsize_out) { | |
218 | SOCK_DEBUG(sk, "X.25: packet size outwards negotiated down"); | |
219 | new->pacsize_out = theirs.pacsize_out; | |
220 | } | |
221 | } | |
222 | ||
223 | if (theirs.winsize_in && theirs.winsize_out) { | |
224 | if (theirs.winsize_in < ours->winsize_in) { | |
225 | SOCK_DEBUG(sk, "X.25: window size inwards negotiated down"); | |
226 | new->winsize_in = theirs.winsize_in; | |
227 | } | |
228 | if (theirs.winsize_out < ours->winsize_out) { | |
229 | SOCK_DEBUG(sk, "X.25: window size outwards negotiated down"); | |
230 | new->winsize_out = theirs.winsize_out; | |
231 | } | |
232 | } | |
233 | ||
234 | return len; | |
235 | } | |
236 | ||
237 | /* | |
238 | * Limit values of certain facilities according to the capability of the | |
239 | * currently attached x25 link. | |
240 | */ | |
241 | void x25_limit_facilities(struct x25_facilities *facilities, | |
242 | struct x25_neigh *nb) | |
243 | { | |
244 | ||
245 | if (!nb->extended) { | |
246 | if (facilities->winsize_in > 7) { | |
247 | printk(KERN_DEBUG "X.25: incoming winsize limited to 7\n"); | |
248 | facilities->winsize_in = 7; | |
249 | } | |
250 | if (facilities->winsize_out > 7) { | |
251 | facilities->winsize_out = 7; | |
252 | printk( KERN_DEBUG "X.25: outgoing winsize limited to 7\n"); | |
253 | } | |
254 | } | |
255 | } |