xref: /openbmc/linux/net/x25/x25_facilities.c (revision baa7eb025ab14f3cba2e35c0a8648f9c9f01d24f)
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.
20  *	apr/14/05	Shaun Pereira - Allow fast select with no restriction
21  *					on response.
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 structures. Unrecognised
32  *	facilities are written to the debug log file.
33  */
34 int x25_parse_facilities(struct sk_buff *skb, struct x25_facilities *facilities,
35 		struct x25_dte_facilities *dte_facs, unsigned long *vc_fac_mask)
36 {
37 	unsigned char *p = skb->data;
38 	unsigned int len;
39 
40 	*vc_fac_mask = 0;
41 
42 	/*
43 	 * The kernel knows which facilities were set on an incoming call but
44 	 * currently this information is not available to userspace.  Here we
45 	 * give userspace who read incoming call facilities 0 length to indicate
46 	 * it wasn't set.
47 	 */
48 	dte_facs->calling_len = 0;
49 	dte_facs->called_len = 0;
50 	memset(dte_facs->called_ae, '\0', sizeof(dte_facs->called_ae));
51 	memset(dte_facs->calling_ae, '\0', sizeof(dte_facs->calling_ae));
52 
53 	if (skb->len < 1)
54 		return 0;
55 
56 	len = *p++;
57 
58 	if (len >= skb->len)
59 		return -1;
60 
61 	while (len > 0) {
62 		switch (*p & X25_FAC_CLASS_MASK) {
63 		case X25_FAC_CLASS_A:
64 			if (len < 2)
65 				return 0;
66 			switch (*p) {
67 			case X25_FAC_REVERSE:
68 				if((p[1] & 0x81) == 0x81) {
69 					facilities->reverse = p[1] & 0x81;
70 					*vc_fac_mask |= X25_MASK_REVERSE;
71 					break;
72 				}
73 
74 				if((p[1] & 0x01) == 0x01) {
75 					facilities->reverse = p[1] & 0x01;
76 					*vc_fac_mask |= X25_MASK_REVERSE;
77 					break;
78 				}
79 
80 				if((p[1] & 0x80) == 0x80) {
81 					facilities->reverse = p[1] & 0x80;
82 					*vc_fac_mask |= X25_MASK_REVERSE;
83 					break;
84 				}
85 
86 				if(p[1] == 0x00) {
87 					facilities->reverse
88 						= X25_DEFAULT_REVERSE;
89 					*vc_fac_mask |= X25_MASK_REVERSE;
90 					break;
91 				}
92 
93 			case X25_FAC_THROUGHPUT:
94 				facilities->throughput = p[1];
95 				*vc_fac_mask |= X25_MASK_THROUGHPUT;
96 				break;
97 			case X25_MARKER:
98 				break;
99 			default:
100 				printk(KERN_DEBUG "X.25: unknown facility "
101 				       "%02X, value %02X\n",
102 				       p[0], p[1]);
103 				break;
104 			}
105 			p   += 2;
106 			len -= 2;
107 			break;
108 		case X25_FAC_CLASS_B:
109 			if (len < 3)
110 				return 0;
111 			switch (*p) {
112 			case X25_FAC_PACKET_SIZE:
113 				facilities->pacsize_in  = p[1];
114 				facilities->pacsize_out = p[2];
115 				*vc_fac_mask |= X25_MASK_PACKET_SIZE;
116 				break;
117 			case X25_FAC_WINDOW_SIZE:
118 				facilities->winsize_in  = p[1];
119 				facilities->winsize_out = p[2];
120 				*vc_fac_mask |= X25_MASK_WINDOW_SIZE;
121 				break;
122 			default:
123 				printk(KERN_DEBUG "X.25: unknown facility "
124 				       "%02X, values %02X, %02X\n",
125 				       p[0], p[1], p[2]);
126 				break;
127 			}
128 			p   += 3;
129 			len -= 3;
130 			break;
131 		case X25_FAC_CLASS_C:
132 			if (len < 4)
133 				return 0;
134 			printk(KERN_DEBUG "X.25: unknown facility %02X, "
135 			       "values %02X, %02X, %02X\n",
136 			       p[0], p[1], p[2], p[3]);
137 			p   += 4;
138 			len -= 4;
139 			break;
140 		case X25_FAC_CLASS_D:
141 			if (len < p[1] + 2)
142 				return 0;
143 			switch (*p) {
144 			case X25_FAC_CALLING_AE:
145 				if (p[1] > X25_MAX_DTE_FACIL_LEN || p[1] <= 1)
146 					return 0;
147 				dte_facs->calling_len = p[2];
148 				memcpy(dte_facs->calling_ae, &p[3], p[1] - 1);
149 				*vc_fac_mask |= X25_MASK_CALLING_AE;
150 				break;
151 			case X25_FAC_CALLED_AE:
152 				if (p[1] > X25_MAX_DTE_FACIL_LEN || p[1] <= 1)
153 					return 0;
154 				dte_facs->called_len = p[2];
155 				memcpy(dte_facs->called_ae, &p[3], p[1] - 1);
156 				*vc_fac_mask |= X25_MASK_CALLED_AE;
157 				break;
158 			default:
159 				printk(KERN_DEBUG "X.25: unknown facility %02X,"
160 					"length %d\n", p[0], p[1]);
161 				break;
162 			}
163 			len -= p[1] + 2;
164 			p += p[1] + 2;
165 			break;
166 		}
167 	}
168 
169 	return p - skb->data;
170 }
171 
172 /*
173  *	Create a set of facilities.
174  */
175 int x25_create_facilities(unsigned char *buffer,
176 		struct x25_facilities *facilities,
177 		struct x25_dte_facilities *dte_facs, unsigned long facil_mask)
178 {
179 	unsigned char *p = buffer + 1;
180 	int len;
181 
182 	if (!facil_mask) {
183 		/*
184 		 * Length of the facilities field in call_req or
185 		 * call_accept packets
186 		 */
187 		buffer[0] = 0;
188 		len = 1; /* 1 byte for the length field */
189 		return len;
190 	}
191 
192 	if (facilities->reverse && (facil_mask & X25_MASK_REVERSE)) {
193 		*p++ = X25_FAC_REVERSE;
194 		*p++ = facilities->reverse;
195 	}
196 
197 	if (facilities->throughput && (facil_mask & X25_MASK_THROUGHPUT)) {
198 		*p++ = X25_FAC_THROUGHPUT;
199 		*p++ = facilities->throughput;
200 	}
201 
202 	if ((facilities->pacsize_in || facilities->pacsize_out) &&
203 	    (facil_mask & X25_MASK_PACKET_SIZE)) {
204 		*p++ = X25_FAC_PACKET_SIZE;
205 		*p++ = facilities->pacsize_in ? : facilities->pacsize_out;
206 		*p++ = facilities->pacsize_out ? : facilities->pacsize_in;
207 	}
208 
209 	if ((facilities->winsize_in || facilities->winsize_out) &&
210 	    (facil_mask & X25_MASK_WINDOW_SIZE)) {
211 		*p++ = X25_FAC_WINDOW_SIZE;
212 		*p++ = facilities->winsize_in ? : facilities->winsize_out;
213 		*p++ = facilities->winsize_out ? : facilities->winsize_in;
214 	}
215 
216 	if (facil_mask & (X25_MASK_CALLING_AE|X25_MASK_CALLED_AE)) {
217 		*p++ = X25_MARKER;
218 		*p++ = X25_DTE_SERVICES;
219 	}
220 
221 	if (dte_facs->calling_len && (facil_mask & X25_MASK_CALLING_AE)) {
222 		unsigned bytecount = (dte_facs->calling_len + 1) >> 1;
223 		*p++ = X25_FAC_CALLING_AE;
224 		*p++ = 1 + bytecount;
225 		*p++ = dte_facs->calling_len;
226 		memcpy(p, dte_facs->calling_ae, bytecount);
227 		p += bytecount;
228 	}
229 
230 	if (dte_facs->called_len && (facil_mask & X25_MASK_CALLED_AE)) {
231 		unsigned bytecount = (dte_facs->called_len % 2) ?
232 		dte_facs->called_len / 2 + 1 :
233 		dte_facs->called_len / 2;
234 		*p++ = X25_FAC_CALLED_AE;
235 		*p++ = 1 + bytecount;
236 		*p++ = dte_facs->called_len;
237 		memcpy(p, dte_facs->called_ae, bytecount);
238 		p+=bytecount;
239 	}
240 
241 	len       = p - buffer;
242 	buffer[0] = len - 1;
243 
244 	return len;
245 }
246 
247 /*
248  *	Try to reach a compromise on a set of facilities.
249  *
250  *	The only real problem is with reverse charging.
251  */
252 int x25_negotiate_facilities(struct sk_buff *skb, struct sock *sk,
253 		struct x25_facilities *new, struct x25_dte_facilities *dte)
254 {
255 	struct x25_sock *x25 = x25_sk(sk);
256 	struct x25_facilities *ours = &x25->facilities;
257 	struct x25_facilities theirs;
258 	int len;
259 
260 	memset(&theirs, 0, sizeof(theirs));
261 	memcpy(new, ours, sizeof(*new));
262 
263 	len = x25_parse_facilities(skb, &theirs, dte, &x25->vc_facil_mask);
264 	if (len < 0)
265 		return len;
266 
267 	/*
268 	 *	They want reverse charging, we won't accept it.
269 	 */
270 	if ((theirs.reverse & 0x01 ) && (ours->reverse & 0x01)) {
271 		SOCK_DEBUG(sk, "X.25: rejecting reverse charging request\n");
272 		return -1;
273 	}
274 
275 	new->reverse = theirs.reverse;
276 
277 	if (theirs.throughput) {
278 		int theirs_in =  theirs.throughput & 0x0f;
279 		int theirs_out = theirs.throughput & 0xf0;
280 		int ours_in  = ours->throughput & 0x0f;
281 		int ours_out = ours->throughput & 0xf0;
282 		if (!ours_in || theirs_in < ours_in) {
283 			SOCK_DEBUG(sk, "X.25: inbound throughput negotiated\n");
284 			new->throughput = (new->throughput & 0xf0) | theirs_in;
285 		}
286 		if (!ours_out || theirs_out < ours_out) {
287 			SOCK_DEBUG(sk,
288 				"X.25: outbound throughput negotiated\n");
289 			new->throughput = (new->throughput & 0x0f) | theirs_out;
290 		}
291 	}
292 
293 	if (theirs.pacsize_in && theirs.pacsize_out) {
294 		if (theirs.pacsize_in < ours->pacsize_in) {
295 			SOCK_DEBUG(sk, "X.25: packet size inwards negotiated down\n");
296 			new->pacsize_in = theirs.pacsize_in;
297 		}
298 		if (theirs.pacsize_out < ours->pacsize_out) {
299 			SOCK_DEBUG(sk, "X.25: packet size outwards negotiated down\n");
300 			new->pacsize_out = theirs.pacsize_out;
301 		}
302 	}
303 
304 	if (theirs.winsize_in && theirs.winsize_out) {
305 		if (theirs.winsize_in < ours->winsize_in) {
306 			SOCK_DEBUG(sk, "X.25: window size inwards negotiated down\n");
307 			new->winsize_in = theirs.winsize_in;
308 		}
309 		if (theirs.winsize_out < ours->winsize_out) {
310 			SOCK_DEBUG(sk, "X.25: window size outwards negotiated down\n");
311 			new->winsize_out = theirs.winsize_out;
312 		}
313 	}
314 
315 	return len;
316 }
317 
318 /*
319  *	Limit values of certain facilities according to the capability of the
320  *      currently attached x25 link.
321  */
322 void x25_limit_facilities(struct x25_facilities *facilities,
323 			  struct x25_neigh *nb)
324 {
325 
326 	if (!nb->extended) {
327 		if (facilities->winsize_in  > 7) {
328 			printk(KERN_DEBUG "X.25: incoming winsize limited to 7\n");
329 			facilities->winsize_in = 7;
330 		}
331 		if (facilities->winsize_out > 7) {
332 			facilities->winsize_out = 7;
333 			printk( KERN_DEBUG "X.25: outgoing winsize limited to 7\n");
334 		}
335 	}
336 }
337