xref: /openbmc/linux/net/x25/x25_facilities.c (revision 1da177e4)
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  */
21 
22 #include <linux/kernel.h>
23 #include <linux/string.h>
24 #include <linux/skbuff.h>
25 #include <net/sock.h>
26 #include <net/x25.h>
27 
28 /*
29  *	Parse a set of facilities into the facilities structure. Unrecognised
30  *	facilities are written to the debug log file.
31  */
32 int x25_parse_facilities(struct sk_buff *skb,
33 			 struct x25_facilities *facilities,
34 			 unsigned long *vc_fac_mask)
35 {
36 	unsigned char *p = skb->data;
37 	unsigned int len = *p++;
38 
39 	*vc_fac_mask = 0;
40 
41 	while (len > 0) {
42 		switch (*p & X25_FAC_CLASS_MASK) {
43 		case X25_FAC_CLASS_A:
44 			switch (*p) {
45 			case X25_FAC_REVERSE:
46 				facilities->reverse = p[1] & 0x01;
47 				*vc_fac_mask |= X25_MASK_REVERSE;
48 				break;
49 			case X25_FAC_THROUGHPUT:
50 				facilities->throughput = p[1];
51 				*vc_fac_mask |= X25_MASK_THROUGHPUT;
52 				break;
53 			default:
54 				printk(KERN_DEBUG "X.25: unknown facility "
55 				       "%02X, value %02X\n",
56 				       p[0], p[1]);
57 				break;
58 			}
59 			p   += 2;
60 			len -= 2;
61 			break;
62 		case X25_FAC_CLASS_B:
63 			switch (*p) {
64 			case X25_FAC_PACKET_SIZE:
65 				facilities->pacsize_in  = p[1];
66 				facilities->pacsize_out = p[2];
67 				*vc_fac_mask |= X25_MASK_PACKET_SIZE;
68 				break;
69 			case X25_FAC_WINDOW_SIZE:
70 				facilities->winsize_in  = p[1];
71 				facilities->winsize_out = p[2];
72 				*vc_fac_mask |= X25_MASK_WINDOW_SIZE;
73 				break;
74 			default:
75 				printk(KERN_DEBUG "X.25: unknown facility "
76 				       "%02X, values %02X, %02X\n",
77 				       p[0], p[1], p[2]);
78 				break;
79 			}
80 			p   += 3;
81 			len -= 3;
82 			break;
83 		case X25_FAC_CLASS_C:
84 			printk(KERN_DEBUG "X.25: unknown facility %02X, "
85 			       "values %02X, %02X, %02X\n",
86 			       p[0], p[1], p[2], p[3]);
87 			p   += 4;
88 			len -= 4;
89 			break;
90 		case X25_FAC_CLASS_D:
91 			printk(KERN_DEBUG "X.25: unknown facility %02X, "
92 			       "length %d, values %02X, %02X, %02X, %02X\n",
93 			       p[0], p[1], p[2], p[3], p[4], p[5]);
94 			len -= p[1] + 2;
95 			p   += p[1] + 2;
96 			break;
97 		}
98 	}
99 
100 	return p - skb->data;
101 }
102 
103 /*
104  *	Create a set of facilities.
105  */
106 int x25_create_facilities(unsigned char *buffer,
107 			  struct x25_facilities *facilities,
108 			  unsigned long facil_mask)
109 {
110 	unsigned char *p = buffer + 1;
111 	int len;
112 
113 	if (!facil_mask) {
114 		/*
115 		 * Length of the facilities field in call_req or
116 		 * call_accept packets
117 		 */
118 		buffer[0] = 0;
119 		len = 1; /* 1 byte for the length field */
120 		return len;
121 	}
122 
123 	if (facilities->reverse && (facil_mask & X25_MASK_REVERSE)) {
124 		*p++ = X25_FAC_REVERSE;
125 		*p++ = !!facilities->reverse;
126 	}
127 
128 	if (facilities->throughput && (facil_mask & X25_MASK_THROUGHPUT)) {
129 		*p++ = X25_FAC_THROUGHPUT;
130 		*p++ = facilities->throughput;
131 	}
132 
133 	if ((facilities->pacsize_in || facilities->pacsize_out) &&
134 	    (facil_mask & X25_MASK_PACKET_SIZE)) {
135 		*p++ = X25_FAC_PACKET_SIZE;
136 		*p++ = facilities->pacsize_in ? : facilities->pacsize_out;
137 		*p++ = facilities->pacsize_out ? : facilities->pacsize_in;
138 	}
139 
140 	if ((facilities->winsize_in || facilities->winsize_out) &&
141 	    (facil_mask & X25_MASK_WINDOW_SIZE)) {
142 		*p++ = X25_FAC_WINDOW_SIZE;
143 		*p++ = facilities->winsize_in ? : facilities->winsize_out;
144 		*p++ = facilities->winsize_out ? : facilities->winsize_in;
145 	}
146 
147 	len       = p - buffer;
148 	buffer[0] = len - 1;
149 
150 	return len;
151 }
152 
153 /*
154  *	Try to reach a compromise on a set of facilities.
155  *
156  *	The only real problem is with reverse charging.
157  */
158 int x25_negotiate_facilities(struct sk_buff *skb, struct sock *sk,
159 			     struct x25_facilities *new)
160 {
161 	struct x25_sock *x25 = x25_sk(sk);
162 	struct x25_facilities *ours = &x25->facilities;
163 	struct x25_facilities theirs;
164 	int len;
165 
166 	memset(&theirs, 0, sizeof(theirs));
167 	memcpy(new, ours, sizeof(*new));
168 
169 	len = x25_parse_facilities(skb, &theirs, &x25->vc_facil_mask);
170 
171 	/*
172 	 *	They want reverse charging, we won't accept it.
173 	 */
174 	if (theirs.reverse && ours->reverse) {
175 		SOCK_DEBUG(sk, "X.25: rejecting reverse charging request");
176 		return -1;
177 	}
178 
179 	new->reverse = theirs.reverse;
180 
181 	if (theirs.throughput) {
182 		if (theirs.throughput < ours->throughput) {
183 			SOCK_DEBUG(sk, "X.25: throughput negotiated down");
184 			new->throughput = theirs.throughput;
185 		}
186 	}
187 
188 	if (theirs.pacsize_in && theirs.pacsize_out) {
189 		if (theirs.pacsize_in < ours->pacsize_in) {
190 			SOCK_DEBUG(sk, "X.25: packet size inwards negotiated down");
191 			new->pacsize_in = theirs.pacsize_in;
192 		}
193 		if (theirs.pacsize_out < ours->pacsize_out) {
194 			SOCK_DEBUG(sk, "X.25: packet size outwards negotiated down");
195 			new->pacsize_out = theirs.pacsize_out;
196 		}
197 	}
198 
199 	if (theirs.winsize_in && theirs.winsize_out) {
200 		if (theirs.winsize_in < ours->winsize_in) {
201 			SOCK_DEBUG(sk, "X.25: window size inwards negotiated down");
202 			new->winsize_in = theirs.winsize_in;
203 		}
204 		if (theirs.winsize_out < ours->winsize_out) {
205 			SOCK_DEBUG(sk, "X.25: window size outwards negotiated down");
206 			new->winsize_out = theirs.winsize_out;
207 		}
208 	}
209 
210 	return len;
211 }
212 
213 /*
214  *	Limit values of certain facilities according to the capability of the
215  *      currently attached x25 link.
216  */
217 void x25_limit_facilities(struct x25_facilities *facilities,
218 			  struct x25_neigh *nb)
219 {
220 
221 	if (!nb->extended) {
222 		if (facilities->winsize_in  > 7) {
223 			printk(KERN_DEBUG "X.25: incoming winsize limited to 7\n");
224 			facilities->winsize_in = 7;
225 		}
226 		if (facilities->winsize_out > 7) {
227 			facilities->winsize_out = 7;
228 			printk( KERN_DEBUG "X.25: outgoing winsize limited to 7\n");
229 		}
230 	}
231 }
232