Linux-2.6.12-rc2
[linux-3.10.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  */
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 }