brute-forced more changes from MontaVista's tree. SCSI partition table read still...
[linux-2.4.git] / net / x25 / x25_route.c
1 /*
2  *      X.25 Packet Layer release 002
3  *
4  *      This is ALPHA test software. This code may break your machine, randomly fail to work with new 
5  *      releases, misbehave and/or generally screw up. It might even work. 
6  *
7  *      This code REQUIRES 2.1.15 or higher
8  *
9  *      This module:
10  *              This module is free software; you can redistribute it and/or
11  *              modify it under the terms of the GNU General Public License
12  *              as published by the Free Software Foundation; either version
13  *              2 of the License, or (at your option) any later version.
14  *
15  *      History
16  *      X.25 001        Jonathan Naylor Started coding.
17  */
18
19 #include <linux/config.h>
20 #include <linux/errno.h>
21 #include <linux/types.h>
22 #include <linux/socket.h>
23 #include <linux/in.h>
24 #include <linux/kernel.h>
25 #include <linux/sched.h>
26 #include <linux/timer.h>
27 #include <linux/string.h>
28 #include <linux/sockios.h>
29 #include <linux/net.h>
30 #include <linux/inet.h>
31 #include <linux/netdevice.h>
32 #include <net/arp.h>
33 #include <linux/if_arp.h>
34 #include <linux/skbuff.h>
35 #include <net/sock.h>
36 #include <asm/segment.h>
37 #include <asm/system.h>
38 #include <asm/uaccess.h>
39 #include <linux/fcntl.h>
40 #include <linux/termios.h>      /* For TIOCINQ/OUTQ */
41 #include <linux/mm.h>
42 #include <linux/interrupt.h>
43 #include <linux/notifier.h>
44 #include <linux/init.h>
45 #include <net/x25.h>
46
47 static struct x25_route *x25_route_list /* = NULL initially */;
48
49 /*
50  *      Add a new route.
51  */
52 static int x25_add_route(x25_address *address, unsigned int sigdigits, struct net_device *dev)
53 {
54         struct x25_route *x25_route;
55         unsigned long flags;
56
57         for (x25_route = x25_route_list; x25_route != NULL; x25_route = x25_route->next)
58                 if (memcmp(&x25_route->address, address, sigdigits) == 0 && x25_route->sigdigits == sigdigits)
59                         return -EINVAL;
60
61         if ((x25_route = kmalloc(sizeof(*x25_route), GFP_ATOMIC)) == NULL)
62                 return -ENOMEM;
63
64         strcpy(x25_route->address.x25_addr, "000000000000000");
65         memcpy(x25_route->address.x25_addr, address->x25_addr, sigdigits);
66
67         x25_route->sigdigits = sigdigits;
68         x25_route->dev       = dev;
69
70         save_flags(flags); cli();
71         x25_route->next = x25_route_list;
72         x25_route_list  = x25_route;
73         restore_flags(flags);
74
75         return 0;
76 }
77
78 static void x25_remove_route(struct x25_route *x25_route)
79 {
80         struct x25_route *s;
81         unsigned long flags;
82
83         save_flags(flags);
84         cli();
85
86         if ((s = x25_route_list) == x25_route) {
87                 x25_route_list = x25_route->next;
88                 restore_flags(flags);
89                 kfree(x25_route);
90                 return;
91         }
92
93         while (s != NULL && s->next != NULL) {
94                 if (s->next == x25_route) {
95                         s->next = x25_route->next;
96                         restore_flags(flags);
97                         kfree(x25_route);
98                         return;
99                 }
100
101                 s = s->next;
102         }
103
104         restore_flags(flags);
105 }
106
107 static int x25_del_route(x25_address *address, unsigned int sigdigits, struct net_device *dev)
108 {
109         struct x25_route *x25_route;
110
111         for (x25_route = x25_route_list; x25_route != NULL; x25_route = x25_route->next) {
112                 if (memcmp(&x25_route->address, address, sigdigits) == 0 && x25_route->sigdigits == sigdigits && x25_route->dev == dev) {
113                         x25_remove_route(x25_route);
114                         return 0;
115                 }
116         }
117
118         return -EINVAL;
119 }
120
121 /*
122  *      A device has been removed, remove its routes.
123  */
124 void x25_route_device_down(struct net_device *dev)
125 {
126         struct x25_route *route, *x25_route = x25_route_list;
127
128         while (x25_route != NULL) {
129                 route     = x25_route;
130                 x25_route = x25_route->next;
131
132                 if (route->dev == dev)
133                         x25_remove_route(route);
134         }
135 }
136
137 /*
138  *      Check that the device given is a valid X.25 interface that is "up".
139  */
140 struct net_device *x25_dev_get(char *devname)
141 {
142         struct net_device *dev;
143
144         if ((dev = dev_get_by_name(devname)) == NULL)
145                 return NULL;
146
147         if ((dev->flags & IFF_UP) && (dev->type == ARPHRD_X25
148 #if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE)
149            || dev->type == ARPHRD_ETHER
150 #endif
151            ))
152                 return dev;
153
154         dev_put(dev);
155
156         return NULL;
157 }
158
159 /*
160  *      Find a device given an X.25 address.
161  */
162 struct net_device *x25_get_route(x25_address *addr)
163 {
164         struct x25_route *route, *use = NULL;
165
166         for (route = x25_route_list; route != NULL; route = route->next) {
167                 if (memcmp(&route->address, addr, route->sigdigits) == 0) {
168                         if (use == NULL) {
169                                 use = route;
170                         } else {
171                                 if (route->sigdigits > use->sigdigits)
172                                         use = route;
173                         }
174                 }
175         }
176
177         return (use != NULL) ? use->dev : NULL;
178 }
179
180 /*
181  *      Handle the ioctls that control the routing functions.
182  */
183 int x25_route_ioctl(unsigned int cmd, void *arg)
184 {
185         struct x25_route_struct x25_route;
186         struct net_device *dev;
187         int err;
188
189         switch (cmd) {
190
191                 case SIOCADDRT:
192                         if (copy_from_user(&x25_route, arg, sizeof(struct x25_route_struct)))
193                                 return -EFAULT;
194                         if (x25_route.sigdigits < 0 || x25_route.sigdigits > 15)
195                                 return -EINVAL;
196                         if ((dev = x25_dev_get(x25_route.device)) == NULL)
197                                 return -EINVAL;
198                         err = x25_add_route(&x25_route.address, x25_route.sigdigits, dev);
199                         dev_put(dev);
200                         return err;
201
202                 case SIOCDELRT:
203                         if (copy_from_user(&x25_route, arg, sizeof(struct x25_route_struct)))
204                                 return -EFAULT;
205                         if (x25_route.sigdigits < 0 || x25_route.sigdigits > 15)
206                                 return -EINVAL;
207                         if ((dev = x25_dev_get(x25_route.device)) == NULL)
208                                 return -EINVAL;
209                         err = x25_del_route(&x25_route.address, x25_route.sigdigits, dev);
210                         dev_put(dev);
211                         return err;
212
213                 default:
214                         return -EINVAL;
215         }
216
217         return 0;
218 }
219
220 int x25_routes_get_info(char *buffer, char **start, off_t offset, int length)
221 {
222         struct x25_route *x25_route;
223         int len     = 0;
224         off_t pos   = 0;
225         off_t begin = 0;
226
227         cli();
228
229         len += sprintf(buffer, "address          digits  device\n");
230
231         for (x25_route = x25_route_list; x25_route != NULL; x25_route = x25_route->next) {
232                 len += sprintf(buffer + len, "%-15s  %-6d  %-5s\n",
233                         x25_route->address.x25_addr,
234                         x25_route->sigdigits,
235                         (x25_route->dev != NULL) ? x25_route->dev->name : "???");
236
237                 pos = begin + len;
238
239                 if (pos < offset) {
240                         len   = 0;
241                         begin = pos;
242                 }
243
244                 if (pos > offset + length)
245                         break;
246         }
247
248         sti();
249
250         *start = buffer + (offset - begin);
251         len   -= (offset - begin);
252
253         if (len > length) len = length;
254
255         return len;
256
257
258 /*
259  *      Release all memory associated with X.25 routing structures.
260  */
261 void __exit x25_route_free(void)
262 {
263         struct x25_route *route, *x25_route = x25_route_list;
264
265         while (x25_route != NULL) {
266                 route     = x25_route;
267                 x25_route = x25_route->next;
268
269                 x25_remove_route(route);
270         }
271 }