Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.25-rc1 398 lines 8.9 kB view raw
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 Jonathan Naylor Started coding. 18 * X.25 002 Jonathan Naylor New timer architecture. 19 * mar/20/00 Daniela Squassoni Disabling/enabling of facilities 20 * negotiation. 21 * 2000-09-04 Henner Eisen dev_hold() / dev_put() for x25_neigh. 22 */ 23 24#include <linux/kernel.h> 25#include <linux/jiffies.h> 26#include <linux/timer.h> 27#include <linux/netdevice.h> 28#include <linux/skbuff.h> 29#include <asm/uaccess.h> 30#include <linux/init.h> 31#include <net/x25.h> 32 33static LIST_HEAD(x25_neigh_list); 34static DEFINE_RWLOCK(x25_neigh_list_lock); 35 36static void x25_t20timer_expiry(unsigned long); 37 38static void x25_transmit_restart_confirmation(struct x25_neigh *nb); 39static void x25_transmit_restart_request(struct x25_neigh *nb); 40 41/* 42 * Linux set/reset timer routines 43 */ 44static inline void x25_start_t20timer(struct x25_neigh *nb) 45{ 46 mod_timer(&nb->t20timer, jiffies + nb->t20); 47} 48 49static void x25_t20timer_expiry(unsigned long param) 50{ 51 struct x25_neigh *nb = (struct x25_neigh *)param; 52 53 x25_transmit_restart_request(nb); 54 55 x25_start_t20timer(nb); 56} 57 58static inline void x25_stop_t20timer(struct x25_neigh *nb) 59{ 60 del_timer(&nb->t20timer); 61} 62 63static inline int x25_t20timer_pending(struct x25_neigh *nb) 64{ 65 return timer_pending(&nb->t20timer); 66} 67 68/* 69 * This handles all restart and diagnostic frames. 70 */ 71void x25_link_control(struct sk_buff *skb, struct x25_neigh *nb, 72 unsigned short frametype) 73{ 74 struct sk_buff *skbn; 75 int confirm; 76 77 switch (frametype) { 78 case X25_RESTART_REQUEST: 79 confirm = !x25_t20timer_pending(nb); 80 x25_stop_t20timer(nb); 81 nb->state = X25_LINK_STATE_3; 82 if (confirm) 83 x25_transmit_restart_confirmation(nb); 84 break; 85 86 case X25_RESTART_CONFIRMATION: 87 x25_stop_t20timer(nb); 88 nb->state = X25_LINK_STATE_3; 89 break; 90 91 case X25_DIAGNOSTIC: 92 printk(KERN_WARNING "x25: diagnostic #%d - " 93 "%02X %02X %02X\n", 94 skb->data[3], skb->data[4], 95 skb->data[5], skb->data[6]); 96 break; 97 98 default: 99 printk(KERN_WARNING "x25: received unknown %02X " 100 "with LCI 000\n", frametype); 101 break; 102 } 103 104 if (nb->state == X25_LINK_STATE_3) 105 while ((skbn = skb_dequeue(&nb->queue)) != NULL) 106 x25_send_frame(skbn, nb); 107} 108 109/* 110 * This routine is called when a Restart Request is needed 111 */ 112static void x25_transmit_restart_request(struct x25_neigh *nb) 113{ 114 unsigned char *dptr; 115 int len = X25_MAX_L2_LEN + X25_STD_MIN_LEN + 2; 116 struct sk_buff *skb = alloc_skb(len, GFP_ATOMIC); 117 118 if (!skb) 119 return; 120 121 skb_reserve(skb, X25_MAX_L2_LEN); 122 123 dptr = skb_put(skb, X25_STD_MIN_LEN + 2); 124 125 *dptr++ = nb->extended ? X25_GFI_EXTSEQ : X25_GFI_STDSEQ; 126 *dptr++ = 0x00; 127 *dptr++ = X25_RESTART_REQUEST; 128 *dptr++ = 0x00; 129 *dptr++ = 0; 130 131 skb->sk = NULL; 132 133 x25_send_frame(skb, nb); 134} 135 136/* 137 * This routine is called when a Restart Confirmation is needed 138 */ 139static void x25_transmit_restart_confirmation(struct x25_neigh *nb) 140{ 141 unsigned char *dptr; 142 int len = X25_MAX_L2_LEN + X25_STD_MIN_LEN; 143 struct sk_buff *skb = alloc_skb(len, GFP_ATOMIC); 144 145 if (!skb) 146 return; 147 148 skb_reserve(skb, X25_MAX_L2_LEN); 149 150 dptr = skb_put(skb, X25_STD_MIN_LEN); 151 152 *dptr++ = nb->extended ? X25_GFI_EXTSEQ : X25_GFI_STDSEQ; 153 *dptr++ = 0x00; 154 *dptr++ = X25_RESTART_CONFIRMATION; 155 156 skb->sk = NULL; 157 158 x25_send_frame(skb, nb); 159} 160 161/* 162 * This routine is called when a Clear Request is needed outside of the context 163 * of a connected socket. 164 */ 165void x25_transmit_clear_request(struct x25_neigh *nb, unsigned int lci, 166 unsigned char cause) 167{ 168 unsigned char *dptr; 169 int len = X25_MAX_L2_LEN + X25_STD_MIN_LEN + 2; 170 struct sk_buff *skb = alloc_skb(len, GFP_ATOMIC); 171 172 if (!skb) 173 return; 174 175 skb_reserve(skb, X25_MAX_L2_LEN); 176 177 dptr = skb_put(skb, X25_STD_MIN_LEN + 2); 178 179 *dptr++ = ((lci >> 8) & 0x0F) | (nb->extended ? 180 X25_GFI_EXTSEQ : 181 X25_GFI_STDSEQ); 182 *dptr++ = (lci >> 0) & 0xFF; 183 *dptr++ = X25_CLEAR_REQUEST; 184 *dptr++ = cause; 185 *dptr++ = 0x00; 186 187 skb->sk = NULL; 188 189 x25_send_frame(skb, nb); 190} 191 192void x25_transmit_link(struct sk_buff *skb, struct x25_neigh *nb) 193{ 194 switch (nb->state) { 195 case X25_LINK_STATE_0: 196 skb_queue_tail(&nb->queue, skb); 197 nb->state = X25_LINK_STATE_1; 198 x25_establish_link(nb); 199 break; 200 case X25_LINK_STATE_1: 201 case X25_LINK_STATE_2: 202 skb_queue_tail(&nb->queue, skb); 203 break; 204 case X25_LINK_STATE_3: 205 x25_send_frame(skb, nb); 206 break; 207 } 208} 209 210/* 211 * Called when the link layer has become established. 212 */ 213void x25_link_established(struct x25_neigh *nb) 214{ 215 switch (nb->state) { 216 case X25_LINK_STATE_0: 217 nb->state = X25_LINK_STATE_2; 218 break; 219 case X25_LINK_STATE_1: 220 x25_transmit_restart_request(nb); 221 nb->state = X25_LINK_STATE_2; 222 x25_start_t20timer(nb); 223 break; 224 } 225} 226 227/* 228 * Called when the link layer has terminated, or an establishment 229 * request has failed. 230 */ 231 232void x25_link_terminated(struct x25_neigh *nb) 233{ 234 nb->state = X25_LINK_STATE_0; 235 /* Out of order: clear existing virtual calls (X.25 03/93 4.6.3) */ 236 x25_kill_by_neigh(nb); 237} 238 239/* 240 * Add a new device. 241 */ 242void x25_link_device_up(struct net_device *dev) 243{ 244 struct x25_neigh *nb = kmalloc(sizeof(*nb), GFP_ATOMIC); 245 246 if (!nb) 247 return; 248 249 skb_queue_head_init(&nb->queue); 250 setup_timer(&nb->t20timer, x25_t20timer_expiry, (unsigned long)nb); 251 252 dev_hold(dev); 253 nb->dev = dev; 254 nb->state = X25_LINK_STATE_0; 255 nb->extended = 0; 256 /* 257 * Enables negotiation 258 */ 259 nb->global_facil_mask = X25_MASK_REVERSE | 260 X25_MASK_THROUGHPUT | 261 X25_MASK_PACKET_SIZE | 262 X25_MASK_WINDOW_SIZE; 263 nb->t20 = sysctl_x25_restart_request_timeout; 264 atomic_set(&nb->refcnt, 1); 265 266 write_lock_bh(&x25_neigh_list_lock); 267 list_add(&nb->node, &x25_neigh_list); 268 write_unlock_bh(&x25_neigh_list_lock); 269} 270 271/** 272 * __x25_remove_neigh - remove neighbour from x25_neigh_list 273 * @nb - neigh to remove 274 * 275 * Remove neighbour from x25_neigh_list. If it was there. 276 * Caller must hold x25_neigh_list_lock. 277 */ 278static void __x25_remove_neigh(struct x25_neigh *nb) 279{ 280 skb_queue_purge(&nb->queue); 281 x25_stop_t20timer(nb); 282 283 if (nb->node.next) { 284 list_del(&nb->node); 285 x25_neigh_put(nb); 286 } 287} 288 289/* 290 * A device has been removed, remove its links. 291 */ 292void x25_link_device_down(struct net_device *dev) 293{ 294 struct x25_neigh *nb; 295 struct list_head *entry, *tmp; 296 297 write_lock_bh(&x25_neigh_list_lock); 298 299 list_for_each_safe(entry, tmp, &x25_neigh_list) { 300 nb = list_entry(entry, struct x25_neigh, node); 301 302 if (nb->dev == dev) { 303 __x25_remove_neigh(nb); 304 dev_put(dev); 305 } 306 } 307 308 write_unlock_bh(&x25_neigh_list_lock); 309} 310 311/* 312 * Given a device, return the neighbour address. 313 */ 314struct x25_neigh *x25_get_neigh(struct net_device *dev) 315{ 316 struct x25_neigh *nb, *use = NULL; 317 struct list_head *entry; 318 319 read_lock_bh(&x25_neigh_list_lock); 320 list_for_each(entry, &x25_neigh_list) { 321 nb = list_entry(entry, struct x25_neigh, node); 322 323 if (nb->dev == dev) { 324 use = nb; 325 break; 326 } 327 } 328 329 if (use) 330 x25_neigh_hold(use); 331 read_unlock_bh(&x25_neigh_list_lock); 332 return use; 333} 334 335/* 336 * Handle the ioctls that control the subscription functions. 337 */ 338int x25_subscr_ioctl(unsigned int cmd, void __user *arg) 339{ 340 struct x25_subscrip_struct x25_subscr; 341 struct x25_neigh *nb; 342 struct net_device *dev; 343 int rc = -EINVAL; 344 345 if (cmd != SIOCX25GSUBSCRIP && cmd != SIOCX25SSUBSCRIP) 346 goto out; 347 348 rc = -EFAULT; 349 if (copy_from_user(&x25_subscr, arg, sizeof(x25_subscr))) 350 goto out; 351 352 rc = -EINVAL; 353 if ((dev = x25_dev_get(x25_subscr.device)) == NULL) 354 goto out; 355 356 if ((nb = x25_get_neigh(dev)) == NULL) 357 goto out_dev_put; 358 359 dev_put(dev); 360 361 if (cmd == SIOCX25GSUBSCRIP) { 362 x25_subscr.extended = nb->extended; 363 x25_subscr.global_facil_mask = nb->global_facil_mask; 364 rc = copy_to_user(arg, &x25_subscr, 365 sizeof(x25_subscr)) ? -EFAULT : 0; 366 } else { 367 rc = -EINVAL; 368 if (!(x25_subscr.extended && x25_subscr.extended != 1)) { 369 rc = 0; 370 nb->extended = x25_subscr.extended; 371 nb->global_facil_mask = x25_subscr.global_facil_mask; 372 } 373 } 374 x25_neigh_put(nb); 375out: 376 return rc; 377out_dev_put: 378 dev_put(dev); 379 goto out; 380} 381 382 383/* 384 * Release all memory associated with X.25 neighbour structures. 385 */ 386void __exit x25_link_free(void) 387{ 388 struct x25_neigh *nb; 389 struct list_head *entry, *tmp; 390 391 write_lock_bh(&x25_neigh_list_lock); 392 393 list_for_each_safe(entry, tmp, &x25_neigh_list) { 394 nb = list_entry(entry, struct x25_neigh, node); 395 __x25_remove_neigh(nb); 396 } 397 write_unlock_bh(&x25_neigh_list_lock); 398}