Merge ../to-linus-stable/
[linux-drm-fsl-dcu.git] / net / x25 / x25_facilities.c
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 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:
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
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;
149                 *p++ = facilities->reverse;
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          */
198         if ((theirs.reverse & 0x01 ) && (ours->reverse & 0x01)) {
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 }