diff options
Diffstat (limited to 'net/rose/af_rose.c')
-rw-r--r-- | net/rose/af_rose.c | 338 |
1 files changed, 157 insertions, 181 deletions
diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c index f3309ade9..bd0ecd843 100644 --- a/net/rose/af_rose.c +++ b/net/rose/af_rose.c @@ -1,8 +1,5 @@ /* - * Rose release 001 - * - * This is ALPHA test software. This code may break your machine, randomly fail to work with new - * releases, misbehave and/or generally screw up. It might even work. + * ROSE release 002 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -13,10 +10,13 @@ * 2 of the License, or (at your option) any later version. * * History - * Rose 001 Jonathan(G4KLX) Cloned from af_netrom.c. + * ROSE 001 Jonathan(G4KLX) Cloned from af_netrom.c. * Alan(GW4PTS) Hacked up for newer API stuff * Terry (VK2KTJ) Added support for variable length * address masks. + * ROSE 002 Jonathan(G4KLX) Changed hdrincl to qbitincl. + * Added random number facilities entry. + * Variable number of ROSE devices. */ #include <linux/config.h> @@ -54,6 +54,8 @@ #include <linux/if_arp.h> #include <linux/init.h> +int rose_ndevs = 6; + int sysctl_rose_restart_request_timeout = ROSE_DEFAULT_T0; int sysctl_rose_call_request_timeout = ROSE_DEFAULT_T1; int sysctl_rose_reset_request_timeout = ROSE_DEFAULT_T2; @@ -62,8 +64,8 @@ int sysctl_rose_no_activity_timeout = ROSE_DEFAULT_IDLE; int sysctl_rose_ack_hold_back_timeout = ROSE_DEFAULT_HB; int sysctl_rose_routing_control = ROSE_DEFAULT_ROUTING; int sysctl_rose_link_fail_timeout = ROSE_DEFAULT_FAIL_TIMEOUT; - -static unsigned int lci = 1; +int sysctl_rose_maximum_vcs = ROSE_DEFAULT_MAXVC; +int sysctl_rose_window_size = ROSE_DEFAULT_WINDOW_SIZE; static struct sock *volatile rose_list = NULL; @@ -72,7 +74,7 @@ static struct proto_ops rose_proto_ops; ax25_address rose_callsign; /* - * Convert a Rose address into text. + * Convert a ROSE address into text. */ char *rose2asc(rose_address *addr) { @@ -94,7 +96,7 @@ char *rose2asc(rose_address *addr) } /* - * Compare two Rose addresses, 0 == equal. + * Compare two ROSE addresses, 0 == equal. */ int rosecmp(rose_address *addr1, rose_address *addr2) { @@ -108,7 +110,7 @@ int rosecmp(rose_address *addr1, rose_address *addr2) } /* - * Compare two Rose addresses for only mask digits, 0 == equal. + * Compare two ROSE addresses for only mask digits, 0 == equal. */ int rosecmpm(rose_address *addr1, rose_address *addr2, unsigned short mask) { @@ -134,7 +136,7 @@ int rosecmpm(rose_address *addr1, rose_address *addr2, unsigned short mask) static void rose_free_sock(struct sock *sk) { - kfree_s(sk->protinfo.rose, sizeof(*sk->protinfo.rose)); + kfree(sk->protinfo.rose); sk_free(sk); @@ -149,7 +151,7 @@ static struct sock *rose_alloc_sock(void) if ((sk = sk_alloc(GFP_ATOMIC)) == NULL) return NULL; - if ((rose = (rose_cb *)kmalloc(sizeof(*rose), GFP_ATOMIC)) == NULL) { + if ((rose = kmalloc(sizeof(*rose), GFP_ATOMIC)) == NULL) { sk_free(sk); return NULL; } @@ -195,6 +197,27 @@ static void rose_remove_socket(struct sock *sk) } /* + * Kill all bound sockets on a broken link layer connection to a + * particular neighbour. + */ +void rose_kill_by_neigh(struct rose_neigh *neigh) +{ + struct sock *s; + + for (s = rose_list; s != NULL; s = s->next) { + if (s->protinfo.rose->neighbour == neigh) { + s->protinfo.rose->state = ROSE_STATE_0; + s->protinfo.rose->neighbour = NULL; + s->state = TCP_CLOSE; + s->err = ENETUNREACH; + s->shutdown |= SEND_SHUTDOWN; + s->state_change(s); + s->dead = 1; + } + } +} + +/* * Kill all bound sockets on a dropped device. */ static void rose_kill_by_device(struct device *dev) @@ -224,9 +247,15 @@ static int rose_device_event(struct notifier_block *this, unsigned long event, v if (event != NETDEV_DOWN) return NOTIFY_DONE; - rose_kill_by_device(dev); - rose_rt_device_down(dev); - rose_link_device_down(dev); + switch (dev->type) { + case ARPHRD_ROSE: + rose_kill_by_device(dev); + break; + case ARPHRD_AX25: + rose_link_device_down(dev); + rose_rt_device_down(dev); + break; + } return NOTIFY_DONE; } @@ -251,7 +280,7 @@ static void rose_insert_socket(struct sock *sk) * Find a socket that wants to accept the Call Request we just * received. */ -static struct sock *rose_find_listener(ax25_address *call) +static struct sock *rose_find_listener(rose_address *addr, ax25_address *call) { unsigned long flags; struct sock *s; @@ -260,14 +289,14 @@ static struct sock *rose_find_listener(ax25_address *call) cli(); for (s = rose_list; s != NULL; s = s->next) { - if (ax25cmp(&s->protinfo.rose->source_call, call) == 0 && s->protinfo.rose->source_ndigis == 0 && s->state == TCP_LISTEN) { + if (rosecmp(&s->protinfo.rose->source_addr, addr) == 0 && ax25cmp(&s->protinfo.rose->source_call, call) == 0 && s->protinfo.rose->source_ndigis == 0 && s->state == TCP_LISTEN) { restore_flags(flags); return s; } } for (s = rose_list; s != NULL; s = s->next) { - if (ax25cmp(&s->protinfo.rose->source_call, &null_ax25_address) == 0 && s->state == TCP_LISTEN) { + if (rosecmp(&s->protinfo.rose->source_addr, addr) == 0 && ax25cmp(&s->protinfo.rose->source_call, &null_ax25_address) == 0 && s->state == TCP_LISTEN) { restore_flags(flags); return s; } @@ -278,9 +307,9 @@ static struct sock *rose_find_listener(ax25_address *call) } /* - * Find a connected Rose socket given my LCI and device. + * Find a connected ROSE socket given my LCI and device. */ -struct sock *rose_find_socket(unsigned int lci, struct device *dev) +struct sock *rose_find_socket(unsigned int lci, struct rose_neigh *neigh) { struct sock *s; unsigned long flags; @@ -289,7 +318,7 @@ struct sock *rose_find_socket(unsigned int lci, struct device *dev) cli(); for (s = rose_list; s != NULL; s = s->next) { - if (s->protinfo.rose->lci == lci && s->protinfo.rose->neighbour->dev == dev) { + if (s->protinfo.rose->lci == lci && s->protinfo.rose->neighbour == neigh) { restore_flags(flags); return s; } @@ -303,17 +332,21 @@ struct sock *rose_find_socket(unsigned int lci, struct device *dev) /* * Find a unique LCI for a given device. */ -unsigned int rose_new_lci(struct device *dev) +unsigned int rose_new_lci(struct rose_neigh *neigh) { - lci++; - if (lci > 4095) lci = 1; + int lci; - while (rose_find_socket(lci, dev) != NULL) { - lci++; - if (lci > 4095) lci = 1; + if (neigh->dce_mode) { + for (lci = 1; lci <= sysctl_rose_maximum_vcs; lci++) + if (rose_find_socket(lci, neigh) == NULL && rose_route_free_lci(lci, neigh) == NULL) + return lci; + } else { + for (lci = sysctl_rose_maximum_vcs; lci > 0; lci--) + if (rose_find_socket(lci, neigh) == NULL && rose_route_free_lci(lci, neigh) == NULL) + return lci; } - return lci; + return 0; } /* @@ -374,94 +407,9 @@ void rose_destroy_socket(struct sock *sk) /* Not static as it's used by the time /* * Handling for system calls applied via the various interfaces to a - * Rose socket object. + * ROSE socket object. */ -/* - * dl1bke 960311: set parameters for existing Rose connections, - * includes a KILL command to abort any connection. - * VERY useful for debugging ;-) - */ -static int rose_ctl_ioctl(const unsigned int cmd, void *arg) -{ - struct rose_ctl_struct rose_ctl; - struct sock *sk; - unsigned long flags; - struct device *dev; - int err; - - if ((err = verify_area(VERIFY_READ, arg, sizeof(rose_ctl))) != 0) - return err; - - copy_from_user(&rose_ctl, arg, sizeof(rose_ctl)); - - if ((dev = rose_ax25_dev_get(rose_ctl.dev)) == NULL) - return -EINVAL; - - if ((sk = rose_find_socket(rose_ctl.lci, dev)) == NULL) - return -ENOTCONN; - - switch (rose_ctl.cmd) { - case ROSE_KILL: - rose_clear_queues(sk); - rose_write_internal(sk, ROSE_CLEAR_REQUEST); - sk->protinfo.rose->state = ROSE_STATE_0; - sk->state = TCP_CLOSE; - sk->err = ENETRESET; - sk->shutdown |= SEND_SHUTDOWN; - if (!sk->dead) - sk->state_change(sk); - sk->dead = 1; - rose_set_timer(sk); - break; - - case ROSE_T1: - if (rose_ctl.arg < 1) - return -EINVAL; - save_flags(flags); cli(); - sk->protinfo.rose->t1 = rose_ctl.arg * ROSE_SLOWHZ; - restore_flags(flags); - break; - - case ROSE_T2: - if (rose_ctl.arg < 1) - return -EINVAL; - save_flags(flags); cli(); - sk->protinfo.rose->t2 = rose_ctl.arg * ROSE_SLOWHZ; - restore_flags(flags); - break; - - case ROSE_T3: - if (rose_ctl.arg < 1) - return -EINVAL; - save_flags(flags); cli(); - sk->protinfo.rose->t3 = rose_ctl.arg * ROSE_SLOWHZ; - restore_flags(flags); - break; - - case ROSE_HOLDBACK: - if (rose_ctl.arg < 1) - return -EINVAL; - save_flags(flags); cli(); - sk->protinfo.rose->hb = rose_ctl.arg * ROSE_SLOWHZ; - restore_flags(flags); - break; - - case ROSE_IDLE: - if (rose_ctl.arg < 1) - return -EINVAL; - save_flags(flags); cli(); - sk->protinfo.rose->idle = rose_ctl.arg * 60 * ROSE_SLOWHZ; - restore_flags(flags); - break; - - default: - return -EINVAL; - } - - return 0; -} - static int rose_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen) { @@ -508,8 +456,8 @@ static int rose_setsockopt(struct socket *sock, int level, int optname, sk->protinfo.rose->idle = opt * 60 * ROSE_SLOWHZ; return 0; - case ROSE_HDRINCL: - sk->protinfo.rose->hdrincl = opt ? 1 : 0; + case ROSE_QBITINCL: + sk->protinfo.rose->qbitincl = opt ? 1 : 0; return 0; default: @@ -551,8 +499,8 @@ static int rose_getsockopt(struct socket *sock, int level, int optname, val = sk->protinfo.rose->idle / (ROSE_SLOWHZ * 60); break; - case ROSE_HDRINCL: - val = sk->protinfo.rose->hdrincl; + case ROSE_QBITINCL: + val = sk->protinfo.rose->qbitincl; break; default: @@ -654,8 +602,8 @@ static struct sock *rose_make_new(struct sock *osk) rose->hb = osk->protinfo.rose->hb; rose->idle = osk->protinfo.rose->idle; - rose->device = osk->protinfo.rose->device; - rose->hdrincl = osk->protinfo.rose->hdrincl; + rose->device = osk->protinfo.rose->device; + rose->qbitincl = osk->protinfo.rose->qbitincl; return sk; } @@ -736,7 +684,7 @@ static int rose_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) return -EINVAL; if ((dev = rose_dev_get(&addr->srose_addr)) == NULL) { - SOCK_DEBUG(sk, "Rose: bind failed: invalid address\n"); + SOCK_DEBUG(sk, "ROSE: bind failed: invalid address\n"); return -EADDRNOTAVAIL; } @@ -760,7 +708,7 @@ static int rose_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) rose_insert_socket(sk); sk->zapped = 0; - SOCK_DEBUG(sk, "Rose: socket is bound\n"); + SOCK_DEBUG(sk, "ROSE: socket is bound\n"); return 0; } @@ -793,7 +741,10 @@ static int rose_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le if (addr->srose_family != AF_ROSE) return -EINVAL; - if ((sk->protinfo.rose->neighbour = rose_get_neigh(&addr->srose_addr)) == NULL) + if ((sk->protinfo.rose->neighbour = rose_get_neigh(&addr->srose_addr)) == NULL) + return -ENETUNREACH; + + if ((sk->protinfo.rose->lci = rose_new_lci(sk->protinfo.rose->neighbour)) == 0) return -ENETUNREACH; if (sk->zapped) { /* Must bind first - autobinding in this may or may not work */ @@ -814,11 +765,12 @@ static int rose_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le sk->protinfo.rose->dest_addr = addr->srose_addr; sk->protinfo.rose->dest_call = addr->srose_call; + sk->protinfo.rose->rand = ((int)sk->protinfo.rose & 0xFFFF) + sk->protinfo.rose->lci; + if (addr->srose_ndigis == 1) { sk->protinfo.rose->dest_ndigis = 1; sk->protinfo.rose->dest_digi = addr->srose_digi; } - sk->protinfo.rose->lci = rose_new_lci(sk->protinfo.rose->neighbour->dev); /* Move to connecting socket, start sending Connect Requests */ sock->state = SS_CONNECTING; @@ -954,7 +906,7 @@ int rose_rx_call_request(struct sk_buff *skb, struct device *dev, struct rose_ne { struct sock *sk; struct sock *make; - rose_cb rose; + struct rose_facilities facilities; skb->sk = NULL; /* Initially we don't know who it's for */ @@ -965,11 +917,11 @@ int rose_rx_call_request(struct sk_buff *skb, struct device *dev, struct rose_ne /* * XXX This is an error. */ - if (!rose_parse_facilities(skb, &rose)) { + if (!rose_parse_facilities(skb, &facilities)) { return 0; } - sk = rose_find_listener(&rose.source_call); + sk = rose_find_listener(&facilities.source_addr, &facilities.source_call); /* * We can't accept the Call Request. @@ -983,14 +935,14 @@ int rose_rx_call_request(struct sk_buff *skb, struct device *dev, struct rose_ne make->state = TCP_ESTABLISHED; make->protinfo.rose->lci = lci; - make->protinfo.rose->dest_addr = rose.dest_addr; - make->protinfo.rose->dest_call = rose.dest_call; - make->protinfo.rose->dest_ndigis = rose.dest_ndigis; - make->protinfo.rose->dest_digi = rose.dest_digi; - make->protinfo.rose->source_addr = rose.source_addr; - make->protinfo.rose->source_call = rose.source_call; - make->protinfo.rose->source_ndigis = rose.source_ndigis; - make->protinfo.rose->source_digi = rose.source_digi; + make->protinfo.rose->dest_addr = facilities.dest_addr; + make->protinfo.rose->dest_call = facilities.dest_call; + make->protinfo.rose->dest_ndigis = facilities.dest_ndigis; + make->protinfo.rose->dest_digi = facilities.dest_digi; + make->protinfo.rose->source_addr = facilities.source_addr; + make->protinfo.rose->source_call = facilities.source_call; + make->protinfo.rose->source_ndigis = facilities.source_ndigis; + make->protinfo.rose->source_digi = facilities.source_digi; make->protinfo.rose->neighbour = neigh; make->protinfo.rose->device = dev; @@ -1026,7 +978,7 @@ static int rose_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct sockaddr_rose srose; struct sk_buff *skb; unsigned char *asmptr; - int size; + int size, qbit = 0; if (msg->msg_flags & ~MSG_DONTWAIT) return -EINVAL; @@ -1039,7 +991,7 @@ static int rose_sendmsg(struct socket *sock, struct msghdr *msg, int len, return -EPIPE; } - if (sk->protinfo.rose->device == NULL) + if (sk->protinfo.rose->neighbour == NULL || sk->protinfo.rose->device == NULL) return -ENETUNREACH; if (usrose != NULL) { @@ -1069,44 +1021,55 @@ static int rose_sendmsg(struct socket *sock, struct msghdr *msg, int len, srose.srose_digi = sk->protinfo.rose->dest_digi; } } - SOCK_DEBUG(sk, "Rose: sendto: Addresses built.\n"); + + SOCK_DEBUG(sk, "ROSE: sendto: Addresses built.\n"); /* Build a packet */ - SOCK_DEBUG(sk, "Rose: sendto: building packet.\n"); + SOCK_DEBUG(sk, "ROSE: sendto: building packet.\n"); size = len + AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN; if ((skb = sock_alloc_send_skb(sk, size, 0, msg->msg_flags & MSG_DONTWAIT, &err)) == NULL) return err; - skb_reserve(skb, size - len); + skb_reserve(skb, AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN); /* - * Push down the Rose header + * Put the data on the end */ + SOCK_DEBUG(sk, "ROSE: Appending user data\n"); - asmptr = skb_push(skb, ROSE_MIN_LEN); - SOCK_DEBUG(sk, "Building Rose Header.\n"); + asmptr = skb->h.raw = skb_put(skb, len); - /* Build a Rose Transport header */ + memcpy_fromiovec(asmptr, msg->msg_iov, len); - *asmptr++ = ((sk->protinfo.rose->lci >> 8) & 0x0F) | ROSE_GFI; - *asmptr++ = (sk->protinfo.rose->lci >> 0) & 0xFF; - *asmptr++ = ROSE_DATA; - SOCK_DEBUG(sk, "Built header.\n"); + /* + * If the Q BIT Include socket option is in force, the first + * byte of the user data is the logical value of the Q Bit. + */ + if (sk->protinfo.rose->qbitincl) { + qbit = skb->data[0]; + skb_pull(skb, 1); + } /* - * Put the data on the end + * Push down the ROSE header */ + asmptr = skb_push(skb, ROSE_MIN_LEN); - skb->h.raw = skb_put(skb, len); + SOCK_DEBUG(sk, "ROSE: Building Network Header.\n"); - asmptr = skb->h.raw; - SOCK_DEBUG(sk, "Rose: Appending user data\n"); + /* Build a ROSE Network header */ + asmptr[0] = ((sk->protinfo.rose->lci >> 8) & 0x0F) | ROSE_GFI; + asmptr[1] = (sk->protinfo.rose->lci >> 0) & 0xFF; + asmptr[2] = ROSE_DATA; - /* User data follows immediately after the Rose transport header */ - memcpy_fromiovec(asmptr, msg->msg_iov, len); - SOCK_DEBUG(sk, "Rose: Transmitting buffer\n"); + if (qbit) + asmptr[0] |= ROSE_Q_BIT; + + SOCK_DEBUG(sk, "ROSE: Built header.\n"); + SOCK_DEBUG(sk, "ROSE: Transmitting buffer\n"); + if (sk->state != TCP_ESTABLISHED) { kfree_skb(skb, FREE_WRITE); return -ENOTCONN; @@ -1123,7 +1086,8 @@ static int rose_recvmsg(struct socket *sock, struct msghdr *msg, int size, { struct sock *sk = sock->sk; struct sockaddr_rose *srose = (struct sockaddr_rose *)msg->msg_name; - int copied; + int copied, qbit; + unsigned char *asmptr; struct sk_buff *skb; int er; @@ -1138,12 +1102,17 @@ static int rose_recvmsg(struct socket *sock, struct msghdr *msg, int size, if ((skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &er)) == NULL) return er; - if (!sk->protinfo.rose->hdrincl) { - skb_pull(skb, ROSE_MIN_LEN); - skb->h.raw = skb->data; + qbit = (skb->data[0] & ROSE_Q_BIT) == ROSE_Q_BIT; + + skb_pull(skb, ROSE_MIN_LEN); + + if (sk->protinfo.rose->qbitincl) { + asmptr = skb_push(skb, 1); + *asmptr = qbit; } - copied = skb->len; + skb->h.raw = skb->data; + copied = skb->len; if (copied > size) { copied = size; @@ -1231,10 +1200,6 @@ static int rose_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) if (!suser()) return -EPERM; return rose_rt_ioctl(cmd, (void *)arg); - case SIOCRSCTLCON: - if (!suser()) return -EPERM; - return rose_ctl_ioctl(cmd, (void *)arg); - case SIOCRSL2CALL: if (!suser()) return -EPERM; if ((err = verify_area(VERIFY_READ, (void *)arg, sizeof(ax25_address))) != 0) @@ -1373,14 +1338,7 @@ static struct proc_dir_entry proc_net_rose_routes = { }; #endif -static struct device dev_rose[] = { - {"rose0", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init}, - {"rose1", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init}, - {"rose2", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init}, - {"rose3", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init}, - {"rose4", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init}, - {"rose5", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init} -}; +static struct device *dev_rose; __initfunc(void rose_proto_init(struct net_proto *pro)) { @@ -1388,17 +1346,26 @@ __initfunc(void rose_proto_init(struct net_proto *pro)) rose_callsign = null_ax25_address; - sock_register(&rose_family_ops); - register_netdevice_notifier(&rose_dev_notifier); - printk(KERN_INFO "G4KLX Rose for Linux. Version 0.1 for AX25.035 Linux 2.1\n"); + if ((dev_rose = kmalloc(rose_ndevs * sizeof(struct device), GFP_KERNEL)) == NULL) { + printk(KERN_ERR "ROSE: rose_proto_init - unable to allocate device structure\n"); + return; + } - if (!ax25_protocol_register(AX25_P_ROSE, rose_route_frame)) - printk(KERN_ERR "Rose unable to register protocol with AX.25\n"); - if (!ax25_linkfail_register(rose_link_failed)) - printk(KERN_ERR "Rose unable to register linkfail handler with AX.25\n"); + memset(dev_rose, 0x00, rose_ndevs * sizeof(struct device)); - for (i = 0; i < 6; i++) + for (i = 0; i < rose_ndevs; i++) { + dev_rose[i].name = kmalloc(20, GFP_KERNEL); + sprintf(dev_rose[i].name, "rose%d", i); + dev_rose[i].init = rose_init; register_netdev(&dev_rose[i]); + } + + sock_register(&rose_family_ops); + register_netdevice_notifier(&rose_dev_notifier); + printk(KERN_INFO "G4KLX ROSE for Linux. Version 0.2 for AX25.035 Linux 2.1\n"); + + ax25_protocol_register(AX25_P_ROSE, rose_route_frame); + ax25_linkfail_register(rose_link_failed); #ifdef CONFIG_SYSCTL rose_register_sysctl(); @@ -1415,6 +1382,12 @@ __initfunc(void rose_proto_init(struct net_proto *pro)) #ifdef MODULE EXPORT_NO_SYMBOLS; +MODULE_PARM(rose_ndevs, "i"); +MODULE_PARM_DESC(rose_ndevs, "number of ROSE devices"); + +MODULE_AUTHOR("Jonathan Naylor G4KLX <g4klx@g4klx.demon.co.uk>"); +MODULE_DESCRIPTION("The amateur radio ROSE network layer protocol"); + int init_module(void) { rose_proto_init(NULL); @@ -1447,13 +1420,16 @@ void cleanup_module(void) sock_unregister(AF_ROSE); - for (i = 0; i < 6; i++) { + for (i = 0; i < rose_ndevs; i++) { if (dev_rose[i].priv != NULL) { kfree(dev_rose[i].priv); dev_rose[i].priv = NULL; unregister_netdev(&dev_rose[i]); } + kfree(dev_rose[i].name); } + + kfree(dev_rose); } #endif |