URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
Compare Revisions
- This comparison shows the changes necessary to convert path
/or1k/trunk/linux/linux-2.4/net/x25
- from Rev 1275 to Rev 1765
- ↔ Reverse comparison
Rev 1275 → Rev 1765
/x25_timer.c
0,0 → 1,192
/* |
* X.25 Packet Layer release 002 |
* |
* 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. |
* |
* This code REQUIRES 2.1.15 or higher |
* |
* This module: |
* This module is free software; you can redistribute it and/or |
* modify it under the terms of the GNU General Public License |
* as published by the Free Software Foundation; either version |
* 2 of the License, or (at your option) any later version. |
* |
* History |
* X.25 001 Jonathan Naylor Started coding. |
* X.25 002 Jonathan Naylor New timer architecture. |
* Centralised disconnection processing. |
*/ |
|
#include <linux/errno.h> |
#include <linux/types.h> |
#include <linux/socket.h> |
#include <linux/in.h> |
#include <linux/kernel.h> |
#include <linux/sched.h> |
#include <linux/timer.h> |
#include <linux/string.h> |
#include <linux/sockios.h> |
#include <linux/net.h> |
#include <linux/inet.h> |
#include <linux/netdevice.h> |
#include <linux/skbuff.h> |
#include <net/sock.h> |
#include <asm/segment.h> |
#include <asm/system.h> |
#include <linux/fcntl.h> |
#include <linux/mm.h> |
#include <linux/interrupt.h> |
#include <net/x25.h> |
|
static void x25_heartbeat_expiry(unsigned long); |
static void x25_timer_expiry(unsigned long); |
|
void x25_start_heartbeat(struct sock *sk) |
{ |
del_timer(&sk->timer); |
|
sk->timer.data = (unsigned long)sk; |
sk->timer.function = &x25_heartbeat_expiry; |
sk->timer.expires = jiffies + 5 * HZ; |
|
add_timer(&sk->timer); |
} |
|
void x25_stop_heartbeat(struct sock *sk) |
{ |
del_timer(&sk->timer); |
} |
|
void x25_start_t2timer(struct sock *sk) |
{ |
del_timer(&sk->protinfo.x25->timer); |
|
sk->protinfo.x25->timer.data = (unsigned long)sk; |
sk->protinfo.x25->timer.function = &x25_timer_expiry; |
sk->protinfo.x25->timer.expires = jiffies + sk->protinfo.x25->t2; |
|
add_timer(&sk->protinfo.x25->timer); |
} |
|
void x25_start_t21timer(struct sock *sk) |
{ |
del_timer(&sk->protinfo.x25->timer); |
|
sk->protinfo.x25->timer.data = (unsigned long)sk; |
sk->protinfo.x25->timer.function = &x25_timer_expiry; |
sk->protinfo.x25->timer.expires = jiffies + sk->protinfo.x25->t21; |
|
add_timer(&sk->protinfo.x25->timer); |
} |
|
void x25_start_t22timer(struct sock *sk) |
{ |
del_timer(&sk->protinfo.x25->timer); |
|
sk->protinfo.x25->timer.data = (unsigned long)sk; |
sk->protinfo.x25->timer.function = &x25_timer_expiry; |
sk->protinfo.x25->timer.expires = jiffies + sk->protinfo.x25->t22; |
|
add_timer(&sk->protinfo.x25->timer); |
} |
|
void x25_start_t23timer(struct sock *sk) |
{ |
del_timer(&sk->protinfo.x25->timer); |
|
sk->protinfo.x25->timer.data = (unsigned long)sk; |
sk->protinfo.x25->timer.function = &x25_timer_expiry; |
sk->protinfo.x25->timer.expires = jiffies + sk->protinfo.x25->t23; |
|
add_timer(&sk->protinfo.x25->timer); |
} |
|
void x25_stop_timer(struct sock *sk) |
{ |
del_timer(&sk->protinfo.x25->timer); |
} |
|
unsigned long x25_display_timer(struct sock *sk) |
{ |
if (!timer_pending(&sk->protinfo.x25->timer)) |
return 0; |
|
return sk->protinfo.x25->timer.expires - jiffies; |
} |
|
static void x25_heartbeat_expiry(unsigned long param) |
{ |
struct sock *sk = (struct sock *)param; |
|
bh_lock_sock(sk); |
if (sk->lock.users) { /* can currently only occur in state 3 */ |
goto restart_heartbeat; |
} |
|
switch (sk->protinfo.x25->state) { |
|
case X25_STATE_0: |
/* Magic here: If we listen() and a new link dies before it |
is accepted() it isn't 'dead' so doesn't get removed. */ |
if (sk->destroy || (sk->state == TCP_LISTEN && sk->dead)) { |
x25_destroy_socket(sk); |
goto unlock; |
} |
break; |
|
case X25_STATE_3: |
/* |
* Check for the state of the receive buffer. |
*/ |
x25_check_rbuf(sk); |
break; |
} |
restart_heartbeat: |
x25_start_heartbeat(sk); |
unlock: |
bh_unlock_sock(sk); |
} |
|
/* |
* Timer has expired, it may have been T2, T21, T22, or T23. We can tell |
* by the state machine state. |
*/ |
static inline void x25_do_timer_expiry(struct sock * sk) |
{ |
switch (sk->protinfo.x25->state) { |
|
case X25_STATE_3: /* T2 */ |
if (sk->protinfo.x25->condition & X25_COND_ACK_PENDING) { |
sk->protinfo.x25->condition &= ~X25_COND_ACK_PENDING; |
x25_enquiry_response(sk); |
} |
break; |
|
case X25_STATE_1: /* T21 */ |
case X25_STATE_4: /* T22 */ |
x25_write_internal(sk, X25_CLEAR_REQUEST); |
sk->protinfo.x25->state = X25_STATE_2; |
x25_start_t23timer(sk); |
break; |
|
case X25_STATE_2: /* T23 */ |
x25_disconnect(sk, ETIMEDOUT, 0, 0); |
break; |
} |
} |
|
static void x25_timer_expiry(unsigned long param) |
{ |
struct sock *sk = (struct sock *)param; |
|
bh_lock_sock(sk); |
if (sk->lock.users) { /* can currently only occur in state 3 */ |
if (sk->protinfo.x25->state == X25_STATE_3) { |
x25_start_t2timer(sk); |
} |
} else { |
x25_do_timer_expiry(sk); |
} |
bh_unlock_sock(sk); |
} |
/x25_in.c
0,0 → 1,369
/* |
* X.25 Packet Layer release 002 |
* |
* 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. |
* |
* This code REQUIRES 2.1.15 or higher |
* |
* This module: |
* This module is free software; you can redistribute it and/or |
* modify it under the terms of the GNU General Public License |
* as published by the Free Software Foundation; either version |
* 2 of the License, or (at your option) any later version. |
* |
* History |
* X.25 001 Jonathan Naylor Started coding. |
* X.25 002 Jonathan Naylor Centralised disconnection code. |
* New timer architecture. |
* 2000-03-20 Daniela Squassoni Disabling/enabling of facilities |
* negotiation. |
* 2000-11-10 Henner Eisen Check and reset for out-of-sequence |
* i-frames. |
*/ |
|
#include <linux/errno.h> |
#include <linux/types.h> |
#include <linux/socket.h> |
#include <linux/in.h> |
#include <linux/kernel.h> |
#include <linux/sched.h> |
#include <linux/timer.h> |
#include <linux/string.h> |
#include <linux/sockios.h> |
#include <linux/net.h> |
#include <linux/inet.h> |
#include <linux/netdevice.h> |
#include <linux/skbuff.h> |
#include <net/sock.h> |
#include <net/ip.h> /* For ip_rcv */ |
#include <asm/segment.h> |
#include <asm/system.h> |
#include <linux/fcntl.h> |
#include <linux/mm.h> |
#include <linux/interrupt.h> |
#include <net/x25.h> |
|
static int x25_queue_rx_frame(struct sock *sk, struct sk_buff *skb, int more) |
{ |
struct sk_buff *skbo, *skbn = skb; |
|
if (more) { |
sk->protinfo.x25->fraglen += skb->len; |
skb_queue_tail(&sk->protinfo.x25->fragment_queue, skb); |
skb_set_owner_r(skb, sk); |
return 0; |
} |
|
if (!more && sk->protinfo.x25->fraglen > 0) { /* End of fragment */ |
int len = sk->protinfo.x25->fraglen + skb->len; |
|
if ((skbn = alloc_skb(len, GFP_ATOMIC)) == NULL){ |
kfree_skb(skb); |
return 1; |
} |
|
skb_queue_tail(&sk->protinfo.x25->fragment_queue, skb); |
|
skbn->h.raw = skbn->data; |
|
skbo = skb_dequeue(&sk->protinfo.x25->fragment_queue); |
memcpy(skb_put(skbn, skbo->len), skbo->data, skbo->len); |
kfree_skb(skbo); |
|
while ((skbo = skb_dequeue(&sk->protinfo.x25->fragment_queue)) != NULL) { |
skb_pull(skbo, (sk->protinfo.x25->neighbour->extended) ? X25_EXT_MIN_LEN : X25_STD_MIN_LEN); |
memcpy(skb_put(skbn, skbo->len), skbo->data, skbo->len); |
kfree_skb(skbo); |
} |
|
sk->protinfo.x25->fraglen = 0; |
} |
|
skb_set_owner_r(skbn, sk); |
skb_queue_tail(&sk->receive_queue, skbn); |
if (!sk->dead) |
sk->data_ready(sk,skbn->len); |
|
return 0; |
} |
|
/* |
* State machine for state 1, Awaiting Call Accepted State. |
* The handling of the timer(s) is in file x25_timer.c. |
* Handling of state 0 and connection release is in af_x25.c. |
*/ |
static int x25_state1_machine(struct sock *sk, struct sk_buff *skb, int frametype) |
{ |
x25_address source_addr, dest_addr; |
|
switch (frametype) { |
|
case X25_CALL_ACCEPTED: |
x25_stop_timer(sk); |
sk->protinfo.x25->condition = 0x00; |
sk->protinfo.x25->vs = 0; |
sk->protinfo.x25->va = 0; |
sk->protinfo.x25->vr = 0; |
sk->protinfo.x25->vl = 0; |
sk->protinfo.x25->state = X25_STATE_3; |
sk->state = TCP_ESTABLISHED; |
/* |
* Parse the data in the frame. |
*/ |
skb_pull(skb, X25_STD_MIN_LEN); |
skb_pull(skb, x25_addr_ntoa(skb->data, &source_addr, &dest_addr)); |
skb_pull(skb, x25_parse_facilities(skb, &sk->protinfo.x25->facilities, &sk->protinfo.x25->vc_facil_mask)); |
/* |
* Copy any Call User Data. |
*/ |
if (skb->len >= 0) { |
memcpy(sk->protinfo.x25->calluserdata.cuddata, skb->data, skb->len); |
sk->protinfo.x25->calluserdata.cudlength = skb->len; |
} |
if (!sk->dead) |
sk->state_change(sk); |
break; |
|
case X25_CLEAR_REQUEST: |
x25_write_internal(sk, X25_CLEAR_CONFIRMATION); |
x25_disconnect(sk, ECONNREFUSED, skb->data[3], skb->data[4]); |
break; |
|
default: |
break; |
} |
|
return 0; |
} |
|
/* |
* State machine for state 2, Awaiting Clear Confirmation State. |
* The handling of the timer(s) is in file x25_timer.c |
* Handling of state 0 and connection release is in af_x25.c. |
*/ |
static int x25_state2_machine(struct sock *sk, struct sk_buff *skb, int frametype) |
{ |
switch (frametype) { |
|
case X25_CLEAR_REQUEST: |
x25_write_internal(sk, X25_CLEAR_CONFIRMATION); |
x25_disconnect(sk, 0, skb->data[3], skb->data[4]); |
break; |
|
case X25_CLEAR_CONFIRMATION: |
x25_disconnect(sk, 0, 0, 0); |
break; |
|
default: |
break; |
} |
|
return 0; |
} |
|
/* |
* State machine for state 3, Connected State. |
* The handling of the timer(s) is in file x25_timer.c |
* Handling of state 0 and connection release is in af_x25.c. |
*/ |
static int x25_state3_machine(struct sock *sk, struct sk_buff *skb, int frametype, int ns, int nr, int q, int d, int m) |
{ |
int queued = 0; |
int modulus; |
|
modulus = (sk->protinfo.x25->neighbour->extended) ? X25_EMODULUS : X25_SMODULUS; |
|
switch (frametype) { |
|
case X25_RESET_REQUEST: |
x25_write_internal(sk, X25_RESET_CONFIRMATION); |
x25_stop_timer(sk); |
sk->protinfo.x25->condition = 0x00; |
sk->protinfo.x25->vs = 0; |
sk->protinfo.x25->vr = 0; |
sk->protinfo.x25->va = 0; |
sk->protinfo.x25->vl = 0; |
x25_requeue_frames(sk); |
break; |
|
case X25_CLEAR_REQUEST: |
x25_write_internal(sk, X25_CLEAR_CONFIRMATION); |
x25_disconnect(sk, 0, skb->data[3], skb->data[4]); |
break; |
|
case X25_RR: |
case X25_RNR: |
if (!x25_validate_nr(sk, nr)) { |
x25_clear_queues(sk); |
x25_write_internal(sk, X25_RESET_REQUEST); |
x25_start_t22timer(sk); |
sk->protinfo.x25->condition = 0x00; |
sk->protinfo.x25->vs = 0; |
sk->protinfo.x25->vr = 0; |
sk->protinfo.x25->va = 0; |
sk->protinfo.x25->vl = 0; |
sk->protinfo.x25->state = X25_STATE_4; |
} else { |
x25_frames_acked(sk, nr); |
if (frametype == X25_RNR) { |
sk->protinfo.x25->condition |= X25_COND_PEER_RX_BUSY; |
} else { |
sk->protinfo.x25->condition &= ~X25_COND_PEER_RX_BUSY; |
} |
} |
break; |
|
case X25_DATA: /* XXX */ |
sk->protinfo.x25->condition &= ~X25_COND_PEER_RX_BUSY; |
if ((ns!=sk->protinfo.x25->vr) || |
!x25_validate_nr(sk, nr)) { |
x25_clear_queues(sk); |
x25_write_internal(sk, X25_RESET_REQUEST); |
x25_start_t22timer(sk); |
sk->protinfo.x25->condition = 0x00; |
sk->protinfo.x25->vs = 0; |
sk->protinfo.x25->vr = 0; |
sk->protinfo.x25->va = 0; |
sk->protinfo.x25->vl = 0; |
sk->protinfo.x25->state = X25_STATE_4; |
break; |
} |
x25_frames_acked(sk, nr); |
if (ns == sk->protinfo.x25->vr) { |
if (x25_queue_rx_frame(sk, skb, m) == 0) { |
sk->protinfo.x25->vr = (sk->protinfo.x25->vr + 1) % modulus; |
queued = 1; |
} else { |
/* Should never happen */ |
x25_clear_queues(sk); |
x25_write_internal(sk, X25_RESET_REQUEST); |
x25_start_t22timer(sk); |
sk->protinfo.x25->condition = 0x00; |
sk->protinfo.x25->vs = 0; |
sk->protinfo.x25->vr = 0; |
sk->protinfo.x25->va = 0; |
sk->protinfo.x25->vl = 0; |
sk->protinfo.x25->state = X25_STATE_4; |
break; |
} |
if (atomic_read(&sk->rmem_alloc) > (sk->rcvbuf / 2)) |
sk->protinfo.x25->condition |= X25_COND_OWN_RX_BUSY; |
} |
/* |
* If the window is full Ack it immediately, else |
* start the holdback timer. |
*/ |
if (((sk->protinfo.x25->vl + sk->protinfo.x25->facilities.winsize_in) % modulus) == sk->protinfo.x25->vr) { |
sk->protinfo.x25->condition &= ~X25_COND_ACK_PENDING; |
x25_stop_timer(sk); |
x25_enquiry_response(sk); |
} else { |
sk->protinfo.x25->condition |= X25_COND_ACK_PENDING; |
x25_start_t2timer(sk); |
} |
break; |
|
case X25_INTERRUPT_CONFIRMATION: |
sk->protinfo.x25->intflag = 0; |
break; |
|
case X25_INTERRUPT: |
if (sk->urginline) { |
queued = (sock_queue_rcv_skb(sk, skb) == 0); |
} else { |
skb_set_owner_r(skb, sk); |
skb_queue_tail(&sk->protinfo.x25->interrupt_in_queue, skb); |
queued = 1; |
} |
if (sk->proc != 0) { |
if (sk->proc > 0) |
kill_proc(sk->proc, SIGURG, 1); |
else |
kill_pg(-sk->proc, SIGURG, 1); |
sock_wake_async(sk->socket, 3, POLL_PRI); |
} |
x25_write_internal(sk, X25_INTERRUPT_CONFIRMATION); |
break; |
|
default: |
printk(KERN_WARNING "x25: unknown %02X in state 3\n", frametype); |
break; |
} |
|
return queued; |
} |
|
/* |
* State machine for state 4, Awaiting Reset Confirmation State. |
* The handling of the timer(s) is in file x25_timer.c |
* Handling of state 0 and connection release is in af_x25.c. |
*/ |
static int x25_state4_machine(struct sock *sk, struct sk_buff *skb, int frametype) |
{ |
switch (frametype) { |
|
case X25_RESET_REQUEST: |
x25_write_internal(sk, X25_RESET_CONFIRMATION); |
case X25_RESET_CONFIRMATION: |
x25_stop_timer(sk); |
sk->protinfo.x25->condition = 0x00; |
sk->protinfo.x25->va = 0; |
sk->protinfo.x25->vr = 0; |
sk->protinfo.x25->vs = 0; |
sk->protinfo.x25->vl = 0; |
sk->protinfo.x25->state = X25_STATE_3; |
x25_requeue_frames(sk); |
break; |
|
case X25_CLEAR_REQUEST: |
x25_write_internal(sk, X25_CLEAR_CONFIRMATION); |
x25_disconnect(sk, 0, skb->data[3], skb->data[4]); |
break; |
|
default: |
break; |
} |
|
return 0; |
} |
|
/* Higher level upcall for a LAPB frame */ |
int x25_process_rx_frame(struct sock *sk, struct sk_buff *skb) |
{ |
int queued = 0, frametype, ns, nr, q, d, m; |
|
if (sk->protinfo.x25->state == X25_STATE_0) |
return 0; |
|
frametype = x25_decode(sk, skb, &ns, &nr, &q, &d, &m); |
|
switch (sk->protinfo.x25->state) { |
case X25_STATE_1: |
queued = x25_state1_machine(sk, skb, frametype); |
break; |
case X25_STATE_2: |
queued = x25_state2_machine(sk, skb, frametype); |
break; |
case X25_STATE_3: |
queued = x25_state3_machine(sk, skb, frametype, ns, nr, q, d, m); |
break; |
case X25_STATE_4: |
queued = x25_state4_machine(sk, skb, frametype); |
break; |
} |
|
x25_kick(sk); |
|
return queued; |
} |
|
int x25_backlog_rcv(struct sock *sk, struct sk_buff *skb) |
{ |
int queued; |
|
queued = x25_process_rx_frame(sk,skb); |
if(!queued) kfree_skb(skb); |
|
return 0; |
} |
/af_x25.c
0,0 → 1,1382
/* |
* X.25 Packet Layer release 002 |
* |
* 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. |
* |
* This code REQUIRES 2.1.15 or higher |
* |
* This module: |
* This module is free software; you can redistribute it and/or |
* modify it under the terms of the GNU General Public License |
* as published by the Free Software Foundation; either version |
* 2 of the License, or (at your option) any later version. |
* |
* History |
* X.25 001 Jonathan Naylor Started coding. |
* X.25 002 Jonathan Naylor Centralised disconnect handling. |
* New timer architecture. |
* 2000-03-11 Henner Eisen MSG_EOR handling more POSIX compliant. |
* 2000-03-22 Daniela Squassoni Allowed disabling/enabling of |
* facilities negotiation and increased |
* the throughput upper limit. |
* 2000-08-27 Arnaldo C. Melo s/suser/capable/ + micro cleanups |
* 2000-09-04 Henner Eisen Set sock->state in x25_accept(). |
* Fixed x25_output() related skb leakage. |
* 2000-10-02 Henner Eisen Made x25_kick() single threaded per socket. |
* 2000-10-27 Henner Eisen MSG_DONTWAIT for fragment allocation. |
* 2000-11-14 Henner Eisen Closing datalink from NETDEV_GOING_DOWN |
*/ |
|
#include <linux/config.h> |
#include <linux/module.h> |
#include <linux/errno.h> |
#include <linux/types.h> |
#include <linux/socket.h> |
#include <linux/in.h> |
#include <linux/kernel.h> |
#include <linux/sched.h> |
#include <linux/timer.h> |
#include <linux/string.h> |
#include <linux/sockios.h> |
#include <linux/net.h> |
#include <linux/stat.h> |
#include <linux/inet.h> |
#include <linux/netdevice.h> |
#include <linux/if_arp.h> |
#include <linux/skbuff.h> |
#include <net/sock.h> |
#include <asm/segment.h> |
#include <asm/system.h> |
#include <asm/uaccess.h> |
#include <linux/fcntl.h> |
#include <linux/termios.h> /* For TIOCINQ/OUTQ */ |
#include <linux/mm.h> |
#include <linux/interrupt.h> |
#include <linux/notifier.h> |
#include <linux/proc_fs.h> |
#include <linux/init.h> |
#include <net/x25.h> |
|
int sysctl_x25_restart_request_timeout = X25_DEFAULT_T20; |
int sysctl_x25_call_request_timeout = X25_DEFAULT_T21; |
int sysctl_x25_reset_request_timeout = X25_DEFAULT_T22; |
int sysctl_x25_clear_request_timeout = X25_DEFAULT_T23; |
int sysctl_x25_ack_holdback_timeout = X25_DEFAULT_T2; |
|
static struct sock *volatile x25_list /* = NULL initially */; |
|
static struct proto_ops x25_proto_ops; |
|
static x25_address null_x25_address = {" "}; |
|
int x25_addr_ntoa(unsigned char *p, x25_address *called_addr, x25_address *calling_addr) |
{ |
int called_len, calling_len; |
char *called, *calling; |
int i; |
|
called_len = (*p >> 0) & 0x0F; |
calling_len = (*p >> 4) & 0x0F; |
|
called = called_addr->x25_addr; |
calling = calling_addr->x25_addr; |
p++; |
|
for (i = 0; i < (called_len + calling_len); i++) { |
if (i < called_len) { |
if (i % 2 != 0) { |
*called++ = ((*p >> 0) & 0x0F) + '0'; |
p++; |
} else { |
*called++ = ((*p >> 4) & 0x0F) + '0'; |
} |
} else { |
if (i % 2 != 0) { |
*calling++ = ((*p >> 0) & 0x0F) + '0'; |
p++; |
} else { |
*calling++ = ((*p >> 4) & 0x0F) + '0'; |
} |
} |
} |
|
*called = '\0'; |
*calling = '\0'; |
|
return 1 + (called_len + calling_len + 1) / 2; |
} |
|
int x25_addr_aton(unsigned char *p, x25_address *called_addr, x25_address *calling_addr) |
{ |
unsigned int called_len, calling_len; |
char *called, *calling; |
int i; |
|
called = called_addr->x25_addr; |
calling = calling_addr->x25_addr; |
|
called_len = strlen(called); |
calling_len = strlen(calling); |
|
*p++ = (calling_len << 4) | (called_len << 0); |
|
for (i = 0; i < (called_len + calling_len); i++) { |
if (i < called_len) { |
if (i % 2 != 0) { |
*p |= (*called++ - '0') << 0; |
p++; |
} else { |
*p = 0x00; |
*p |= (*called++ - '0') << 4; |
} |
} else { |
if (i % 2 != 0) { |
*p |= (*calling++ - '0') << 0; |
p++; |
} else { |
*p = 0x00; |
*p |= (*calling++ - '0') << 4; |
} |
} |
} |
|
return 1 + (called_len + calling_len + 1) / 2; |
} |
|
/* |
* Socket removal during an interrupt is now safe. |
*/ |
static void x25_remove_socket(struct sock *sk) |
{ |
struct sock *s; |
unsigned long flags; |
|
save_flags(flags); |
cli(); |
|
if ((s = x25_list) == sk) { |
x25_list = s->next; |
restore_flags(flags); |
return; |
} |
|
while (s != NULL && s->next != NULL) { |
if (s->next == sk) { |
s->next = sk->next; |
restore_flags(flags); |
return; |
} |
|
s = s->next; |
} |
|
restore_flags(flags); |
} |
|
/* |
* Kill all bound sockets on a dropped device. |
*/ |
static void x25_kill_by_device(struct net_device *dev) |
{ |
struct sock *s; |
|
for (s = x25_list; s != NULL; s = s->next) |
if (s->protinfo.x25->neighbour && |
s->protinfo.x25->neighbour->dev == dev) |
x25_disconnect(s, ENETUNREACH, 0, 0); |
} |
|
/* |
* Handle device status changes. |
*/ |
static int x25_device_event(struct notifier_block *this, unsigned long event, void *ptr) |
{ |
struct net_device *dev = (struct net_device *)ptr; |
struct x25_neigh *neigh; |
|
if (dev->type == ARPHRD_X25 |
#if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE) |
|| dev->type == ARPHRD_ETHER |
#endif |
) { |
switch (event) { |
case NETDEV_UP: |
x25_link_device_up(dev); |
break; |
case NETDEV_GOING_DOWN: |
if ((neigh = x25_get_neigh(dev))) |
x25_terminate_link(neigh); |
break; |
case NETDEV_DOWN: |
x25_kill_by_device(dev); |
x25_route_device_down(dev); |
x25_link_device_down(dev); |
break; |
} |
} |
|
return NOTIFY_DONE; |
} |
|
/* |
* Add a socket to the bound sockets list. |
*/ |
static void x25_insert_socket(struct sock *sk) |
{ |
unsigned long flags; |
|
save_flags(flags); |
cli(); |
|
sk->next = x25_list; |
x25_list = sk; |
|
restore_flags(flags); |
} |
|
/* |
* Find a socket that wants to accept the Call Request we just |
* received. |
*/ |
static struct sock *x25_find_listener(x25_address *addr) |
{ |
unsigned long flags; |
struct sock *s; |
|
save_flags(flags); |
cli(); |
|
for (s = x25_list; s != NULL; s = s->next) { |
if ((strcmp(addr->x25_addr, s->protinfo.x25->source_addr.x25_addr) == 0 || |
strcmp(addr->x25_addr, null_x25_address.x25_addr) == 0) && |
s->state == TCP_LISTEN) { |
restore_flags(flags); |
return s; |
} |
} |
|
restore_flags(flags); |
return NULL; |
} |
|
/* |
* Find a connected X.25 socket given my LCI and neighbour. |
*/ |
struct sock *x25_find_socket(unsigned int lci, struct x25_neigh *neigh) |
{ |
struct sock *s; |
unsigned long flags; |
|
save_flags(flags); |
cli(); |
|
for (s = x25_list; s != NULL; s = s->next) { |
if (s->protinfo.x25->lci == lci && s->protinfo.x25->neighbour == neigh) { |
restore_flags(flags); |
return s; |
} |
} |
|
restore_flags(flags); |
return NULL; |
} |
|
/* |
* Find a unique LCI for a given device. |
*/ |
unsigned int x25_new_lci(struct x25_neigh *neigh) |
{ |
unsigned int lci = 1; |
|
while (x25_find_socket(lci, neigh) != NULL) { |
lci++; |
if (lci == 4096) return 0; |
} |
|
return lci; |
} |
|
/* |
* Deferred destroy. |
*/ |
void x25_destroy_socket(struct sock *); |
|
/* |
* handler for deferred kills. |
*/ |
static void x25_destroy_timer(unsigned long data) |
{ |
x25_destroy_socket((struct sock *)data); |
} |
|
/* |
* This is called from user mode and the timers. Thus it protects itself against |
* interrupt users but doesn't worry about being called during work. |
* Once it is removed from the queue no interrupt or bottom half will |
* touch it and we are (fairly 8-) ) safe. |
*/ |
void x25_destroy_socket(struct sock *sk) /* Not static as it's used by the timer */ |
{ |
struct sk_buff *skb; |
unsigned long flags; |
|
save_flags(flags); |
cli(); |
|
x25_stop_heartbeat(sk); |
x25_stop_timer(sk); |
|
x25_remove_socket(sk); |
x25_clear_queues(sk); /* Flush the queues */ |
|
while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) { |
if (skb->sk != sk) { /* A pending connection */ |
skb->sk->dead = 1; /* Queue the unaccepted socket for death */ |
x25_start_heartbeat(skb->sk); |
skb->sk->protinfo.x25->state = X25_STATE_0; |
} |
|
kfree_skb(skb); |
} |
|
if (atomic_read(&sk->wmem_alloc) != 0 || atomic_read(&sk->rmem_alloc) != 0) { |
/* Defer: outstanding buffers */ |
init_timer(&sk->timer); |
sk->timer.expires = jiffies + 10 * HZ; |
sk->timer.function = x25_destroy_timer; |
sk->timer.data = (unsigned long)sk; |
add_timer(&sk->timer); |
} else { |
sk_free(sk); |
MOD_DEC_USE_COUNT; |
} |
|
restore_flags(flags); |
} |
|
/* |
* Handling for system calls applied via the various interfaces to a |
* X.25 socket object. |
*/ |
|
static int x25_setsockopt(struct socket *sock, int level, int optname, |
char *optval, int optlen) |
{ |
struct sock *sk = sock->sk; |
int opt; |
|
if (level != SOL_X25) |
return -ENOPROTOOPT; |
|
if (optlen < sizeof(int)) |
return-EINVAL; |
|
if (get_user(opt, (int *)optval)) |
return -EFAULT; |
|
switch (optname) { |
case X25_QBITINCL: |
sk->protinfo.x25->qbitincl = opt ? 1 : 0; |
return 0; |
|
default: |
return -ENOPROTOOPT; |
} |
} |
|
static int x25_getsockopt(struct socket *sock, int level, int optname, |
char *optval, int *optlen) |
{ |
struct sock *sk = sock->sk; |
int val = 0; |
int len; |
|
if (level != SOL_X25) |
return -ENOPROTOOPT; |
|
if (get_user(len, optlen)) |
return -EFAULT; |
|
switch (optname) { |
case X25_QBITINCL: |
val = sk->protinfo.x25->qbitincl; |
break; |
|
default: |
return -ENOPROTOOPT; |
} |
|
len = min_t(unsigned int, len, sizeof(int)); |
|
if (len < 0) |
return -EINVAL; |
|
if (put_user(len, optlen)) |
return -EFAULT; |
|
return copy_to_user(optval, &val, len) ? -EFAULT : 0; |
} |
|
static int x25_listen(struct socket *sock, int backlog) |
{ |
struct sock *sk = sock->sk; |
|
if (sk->state != TCP_LISTEN) { |
memset(&sk->protinfo.x25->dest_addr, '\0', X25_ADDR_LEN); |
sk->max_ack_backlog = backlog; |
sk->state = TCP_LISTEN; |
return 0; |
} |
|
return -EOPNOTSUPP; |
} |
|
static struct sock *x25_alloc_socket(void) |
{ |
struct sock *sk; |
x25_cb *x25; |
|
if ((sk = sk_alloc(AF_X25, GFP_ATOMIC, 1)) == NULL) |
return NULL; |
|
if ((x25 = kmalloc(sizeof(*x25), GFP_ATOMIC)) == NULL) { |
sk_free(sk); |
return NULL; |
} |
|
memset(x25, 0x00, sizeof(*x25)); |
|
x25->sk = sk; |
sk->protinfo.x25 = x25; |
|
MOD_INC_USE_COUNT; |
|
sock_init_data(NULL, sk); |
|
skb_queue_head_init(&x25->ack_queue); |
skb_queue_head_init(&x25->fragment_queue); |
skb_queue_head_init(&x25->interrupt_in_queue); |
skb_queue_head_init(&x25->interrupt_out_queue); |
|
return sk; |
} |
|
static int x25_create(struct socket *sock, int protocol) |
{ |
struct sock *sk; |
x25_cb *x25; |
|
if (sock->type != SOCK_SEQPACKET || protocol != 0) |
return -ESOCKTNOSUPPORT; |
|
if ((sk = x25_alloc_socket()) == NULL) |
return -ENOMEM; |
|
x25 = sk->protinfo.x25; |
|
sock_init_data(sock, sk); |
|
init_timer(&x25->timer); |
|
sock->ops = &x25_proto_ops; |
sk->protocol = protocol; |
sk->backlog_rcv = x25_backlog_rcv; |
|
x25->t21 = sysctl_x25_call_request_timeout; |
x25->t22 = sysctl_x25_reset_request_timeout; |
x25->t23 = sysctl_x25_clear_request_timeout; |
x25->t2 = sysctl_x25_ack_holdback_timeout; |
x25->state = X25_STATE_0; |
|
x25->facilities.winsize_in = X25_DEFAULT_WINDOW_SIZE; |
x25->facilities.winsize_out = X25_DEFAULT_WINDOW_SIZE; |
x25->facilities.pacsize_in = X25_DEFAULT_PACKET_SIZE; |
x25->facilities.pacsize_out = X25_DEFAULT_PACKET_SIZE; |
x25->facilities.throughput = X25_DEFAULT_THROUGHPUT; |
x25->facilities.reverse = X25_DEFAULT_REVERSE; |
|
return 0; |
} |
|
static struct sock *x25_make_new(struct sock *osk) |
{ |
struct sock *sk; |
x25_cb *x25; |
|
if (osk->type != SOCK_SEQPACKET) |
return NULL; |
|
if ((sk = x25_alloc_socket()) == NULL) |
return NULL; |
|
x25 = sk->protinfo.x25; |
|
sk->type = osk->type; |
sk->socket = osk->socket; |
sk->priority = osk->priority; |
sk->protocol = osk->protocol; |
sk->rcvbuf = osk->rcvbuf; |
sk->sndbuf = osk->sndbuf; |
sk->debug = osk->debug; |
sk->state = TCP_ESTABLISHED; |
sk->sleep = osk->sleep; |
sk->zapped = osk->zapped; |
sk->backlog_rcv = osk->backlog_rcv; |
|
x25->t21 = osk->protinfo.x25->t21; |
x25->t22 = osk->protinfo.x25->t22; |
x25->t23 = osk->protinfo.x25->t23; |
x25->t2 = osk->protinfo.x25->t2; |
|
x25->facilities = osk->protinfo.x25->facilities; |
|
x25->qbitincl = osk->protinfo.x25->qbitincl; |
|
init_timer(&x25->timer); |
|
return sk; |
} |
|
static int x25_release(struct socket *sock) |
{ |
struct sock *sk = sock->sk; |
|
if (sk == NULL) return 0; |
|
switch (sk->protinfo.x25->state) { |
|
case X25_STATE_0: |
case X25_STATE_2: |
x25_disconnect(sk, 0, 0, 0); |
x25_destroy_socket(sk); |
break; |
|
case X25_STATE_1: |
case X25_STATE_3: |
case X25_STATE_4: |
x25_clear_queues(sk); |
x25_write_internal(sk, X25_CLEAR_REQUEST); |
x25_start_t23timer(sk); |
sk->protinfo.x25->state = X25_STATE_2; |
sk->state = TCP_CLOSE; |
sk->shutdown |= SEND_SHUTDOWN; |
sk->state_change(sk); |
sk->dead = 1; |
sk->destroy = 1; |
break; |
|
default: |
break; |
} |
|
sock->sk = NULL; |
sk->socket = NULL; /* Not used, but we should do this */ |
|
return 0; |
} |
|
static int x25_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) |
{ |
struct sock *sk = sock->sk; |
struct sockaddr_x25 *addr = (struct sockaddr_x25 *)uaddr; |
|
if (sk->zapped == 0) |
return -EINVAL; |
|
if (addr_len != sizeof(struct sockaddr_x25)) |
return -EINVAL; |
|
if (addr->sx25_family != AF_X25) |
return -EINVAL; |
|
sk->protinfo.x25->source_addr = addr->sx25_addr; |
|
x25_insert_socket(sk); |
|
sk->zapped = 0; |
|
SOCK_DEBUG(sk, "x25_bind: socket is bound\n"); |
|
return 0; |
} |
|
static int x25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags) |
{ |
struct sock *sk = sock->sk; |
struct sockaddr_x25 *addr = (struct sockaddr_x25 *)uaddr; |
struct net_device *dev; |
|
if (sk->state == TCP_ESTABLISHED && sock->state == SS_CONNECTING) { |
sock->state = SS_CONNECTED; |
return 0; /* Connect completed during a ERESTARTSYS event */ |
} |
|
if (sk->state == TCP_CLOSE && sock->state == SS_CONNECTING) { |
sock->state = SS_UNCONNECTED; |
return -ECONNREFUSED; |
} |
|
if (sk->state == TCP_ESTABLISHED) |
return -EISCONN; /* No reconnect on a seqpacket socket */ |
|
sk->state = TCP_CLOSE; |
sock->state = SS_UNCONNECTED; |
|
if (addr_len != sizeof(struct sockaddr_x25)) |
return -EINVAL; |
|
if (addr->sx25_family != AF_X25) |
return -EINVAL; |
|
if ((dev = x25_get_route(&addr->sx25_addr)) == NULL) |
return -ENETUNREACH; |
|
if ((sk->protinfo.x25->neighbour = x25_get_neigh(dev)) == NULL) |
return -ENETUNREACH; |
|
x25_limit_facilities(&sk->protinfo.x25->facilities, |
sk->protinfo.x25->neighbour); |
|
if ((sk->protinfo.x25->lci = x25_new_lci(sk->protinfo.x25->neighbour)) == 0) |
return -ENETUNREACH; |
|
if (sk->zapped) /* Must bind first - autobinding does not work */ |
return -EINVAL; |
|
if (strcmp(sk->protinfo.x25->source_addr.x25_addr, null_x25_address.x25_addr) == 0) |
memset(&sk->protinfo.x25->source_addr, '\0', X25_ADDR_LEN); |
|
sk->protinfo.x25->dest_addr = addr->sx25_addr; |
|
/* Move to connecting socket, start sending Connect Requests */ |
sock->state = SS_CONNECTING; |
sk->state = TCP_SYN_SENT; |
|
sk->protinfo.x25->state = X25_STATE_1; |
|
x25_write_internal(sk, X25_CALL_REQUEST); |
|
x25_start_heartbeat(sk); |
x25_start_t21timer(sk); |
|
/* Now the loop */ |
if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) |
return -EINPROGRESS; |
|
cli(); /* To avoid races on the sleep */ |
|
/* |
* A Clear Request or timeout or failed routing will go to closed. |
*/ |
while (sk->state == TCP_SYN_SENT) { |
interruptible_sleep_on(sk->sleep); |
if (signal_pending(current)) { |
sti(); |
return -ERESTARTSYS; |
} |
} |
|
if (sk->state != TCP_ESTABLISHED) { |
sti(); |
sock->state = SS_UNCONNECTED; |
return sock_error(sk); /* Always set at this point */ |
} |
|
sock->state = SS_CONNECTED; |
|
sti(); |
|
return 0; |
} |
|
static int x25_accept(struct socket *sock, struct socket *newsock, int flags) |
{ |
struct sock *sk; |
struct sock *newsk; |
struct sk_buff *skb; |
|
if ((sk = sock->sk) == NULL) |
return -EINVAL; |
|
if (sk->type != SOCK_SEQPACKET) |
return -EOPNOTSUPP; |
|
if (sk->state != TCP_LISTEN) |
return -EINVAL; |
|
/* |
* The write queue this time is holding sockets ready to use |
* hooked into the CALL INDICATION we saved |
*/ |
do { |
cli(); |
if ((skb = skb_dequeue(&sk->receive_queue)) == NULL) { |
if (flags & O_NONBLOCK) { |
sti(); |
return -EWOULDBLOCK; |
} |
interruptible_sleep_on(sk->sleep); |
if (signal_pending(current)) { |
sti(); |
return -ERESTARTSYS; |
} |
} |
} while (skb == NULL); |
|
newsk = skb->sk; |
newsk->pair = NULL; |
newsk->socket = newsock; |
newsk->sleep = &newsock->wait; |
sti(); |
|
/* Now attach up the new socket */ |
skb->sk = NULL; |
kfree_skb(skb); |
sk->ack_backlog--; |
newsock->sk = newsk; |
newsock->state = SS_CONNECTED; |
|
return 0; |
} |
|
static int x25_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, int peer) |
{ |
struct sockaddr_x25 *sx25 = (struct sockaddr_x25 *)uaddr; |
struct sock *sk = sock->sk; |
|
if (peer != 0) { |
if (sk->state != TCP_ESTABLISHED) |
return -ENOTCONN; |
sx25->sx25_addr = sk->protinfo.x25->dest_addr; |
} else { |
sx25->sx25_addr = sk->protinfo.x25->source_addr; |
} |
|
sx25->sx25_family = AF_X25; |
*uaddr_len = sizeof(struct sockaddr_x25); |
|
return 0; |
} |
|
int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *neigh, unsigned int lci) |
{ |
struct sock *sk; |
struct sock *make; |
x25_address source_addr, dest_addr; |
struct x25_facilities facilities; |
int len; |
|
/* |
* Remove the LCI and frame type. |
*/ |
skb_pull(skb, X25_STD_MIN_LEN); |
|
/* |
* Extract the X.25 addresses and convert them to ASCII strings, |
* and remove them. |
*/ |
skb_pull(skb, x25_addr_ntoa(skb->data, &source_addr, &dest_addr)); |
|
/* |
* Find a listener for the particular address. |
*/ |
sk = x25_find_listener(&source_addr); |
|
/* |
* We can't accept the Call Request. |
*/ |
if (sk == NULL || sk->ack_backlog == sk->max_ack_backlog) { |
x25_transmit_clear_request(neigh, lci, 0x01); |
return 0; |
} |
|
/* |
* Try to reach a compromise on the requested facilities. |
*/ |
if ((len = x25_negotiate_facilities(skb, sk, &facilities)) == -1) { |
x25_transmit_clear_request(neigh, lci, 0x01); |
return 0; |
} |
|
/* |
* current neighbour/link might impose additional limits |
* on certain facilties |
*/ |
|
x25_limit_facilities(&facilities,neigh); |
|
/* |
* Try to create a new socket. |
*/ |
if ((make = x25_make_new(sk)) == NULL) { |
x25_transmit_clear_request(neigh, lci, 0x01); |
return 0; |
} |
|
/* |
* Remove the facilities, leaving any Call User Data. |
*/ |
skb_pull(skb, len); |
|
skb->sk = make; |
make->state = TCP_ESTABLISHED; |
|
make->protinfo.x25->lci = lci; |
make->protinfo.x25->dest_addr = dest_addr; |
make->protinfo.x25->source_addr = source_addr; |
make->protinfo.x25->neighbour = neigh; |
make->protinfo.x25->facilities = facilities; |
make->protinfo.x25->vc_facil_mask = sk->protinfo.x25->vc_facil_mask; |
|
x25_write_internal(make, X25_CALL_ACCEPTED); |
|
/* |
* Incoming Call User Data. |
*/ |
if (skb->len >= 0) { |
memcpy(make->protinfo.x25->calluserdata.cuddata, skb->data, skb->len); |
make->protinfo.x25->calluserdata.cudlength = skb->len; |
} |
|
make->protinfo.x25->state = X25_STATE_3; |
|
sk->ack_backlog++; |
make->pair = sk; |
|
x25_insert_socket(make); |
|
skb_queue_head(&sk->receive_queue, skb); |
|
x25_start_heartbeat(make); |
|
if (!sk->dead) |
sk->data_ready(sk, skb->len); |
|
return 1; |
} |
|
static int x25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm) |
{ |
struct sock *sk = sock->sk; |
struct sockaddr_x25 *usx25 = (struct sockaddr_x25 *)msg->msg_name; |
int err; |
struct sockaddr_x25 sx25; |
struct sk_buff *skb; |
unsigned char *asmptr; |
int size, qbit = 0; |
|
if (msg->msg_flags & ~(MSG_DONTWAIT | MSG_OOB | MSG_EOR)) |
return -EINVAL; |
|
/* we currently don't support segmented records at the user interface */ |
if (!(msg->msg_flags & (MSG_EOR|MSG_OOB))) |
return -EINVAL; |
|
if (sk->zapped) |
return -EADDRNOTAVAIL; |
|
if (sk->shutdown & SEND_SHUTDOWN) { |
send_sig(SIGPIPE, current, 0); |
return -EPIPE; |
} |
|
if (sk->protinfo.x25->neighbour == NULL) |
return -ENETUNREACH; |
|
if (usx25 != NULL) { |
if (msg->msg_namelen < sizeof(sx25)) |
return -EINVAL; |
sx25 = *usx25; |
if (strcmp(sk->protinfo.x25->dest_addr.x25_addr, sx25.sx25_addr.x25_addr) != 0) |
return -EISCONN; |
if (sx25.sx25_family != AF_X25) |
return -EINVAL; |
} else { |
/* |
* FIXME 1003.1g - if the socket is like this because |
* it has become closed (not started closed) we ought |
* to SIGPIPE, EPIPE; |
*/ |
if (sk->state != TCP_ESTABLISHED) |
return -ENOTCONN; |
|
sx25.sx25_family = AF_X25; |
sx25.sx25_addr = sk->protinfo.x25->dest_addr; |
} |
|
SOCK_DEBUG(sk, "x25_sendmsg: sendto: Addresses built.\n"); |
|
/* Build a packet */ |
SOCK_DEBUG(sk, "x25_sendmsg: sendto: building packet.\n"); |
|
if ((msg->msg_flags & MSG_OOB) && len > 32) |
len = 32; |
|
size = len + X25_MAX_L2_LEN + X25_EXT_MIN_LEN; |
|
if ((skb = sock_alloc_send_skb(sk, size, msg->msg_flags & MSG_DONTWAIT, &err)) == NULL) |
return err; |
X25_SKB_CB(skb)->flags = msg->msg_flags; |
|
skb_reserve(skb, X25_MAX_L2_LEN + X25_EXT_MIN_LEN); |
|
/* |
* Put the data on the end |
*/ |
SOCK_DEBUG(sk, "x25_sendmsg: Copying user data\n"); |
|
asmptr = skb->h.raw = skb_put(skb, len); |
|
memcpy_fromiovec(asmptr, msg->msg_iov, len); |
|
/* |
* 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.x25->qbitincl) { |
qbit = skb->data[0]; |
skb_pull(skb, 1); |
} |
|
/* |
* Push down the X.25 header |
*/ |
SOCK_DEBUG(sk, "x25_sendmsg: Building X.25 Header.\n"); |
|
if (msg->msg_flags & MSG_OOB) { |
if (sk->protinfo.x25->neighbour->extended) { |
asmptr = skb_push(skb, X25_STD_MIN_LEN); |
*asmptr++ = ((sk->protinfo.x25->lci >> 8) & 0x0F) | X25_GFI_EXTSEQ; |
*asmptr++ = (sk->protinfo.x25->lci >> 0) & 0xFF; |
*asmptr++ = X25_INTERRUPT; |
} else { |
asmptr = skb_push(skb, X25_STD_MIN_LEN); |
*asmptr++ = ((sk->protinfo.x25->lci >> 8) & 0x0F) | X25_GFI_STDSEQ; |
*asmptr++ = (sk->protinfo.x25->lci >> 0) & 0xFF; |
*asmptr++ = X25_INTERRUPT; |
} |
} else { |
if (sk->protinfo.x25->neighbour->extended) { |
/* Build an Extended X.25 header */ |
asmptr = skb_push(skb, X25_EXT_MIN_LEN); |
*asmptr++ = ((sk->protinfo.x25->lci >> 8) & 0x0F) | X25_GFI_EXTSEQ; |
*asmptr++ = (sk->protinfo.x25->lci >> 0) & 0xFF; |
*asmptr++ = X25_DATA; |
*asmptr++ = X25_DATA; |
} else { |
/* Build an Standard X.25 header */ |
asmptr = skb_push(skb, X25_STD_MIN_LEN); |
*asmptr++ = ((sk->protinfo.x25->lci >> 8) & 0x0F) | X25_GFI_STDSEQ; |
*asmptr++ = (sk->protinfo.x25->lci >> 0) & 0xFF; |
*asmptr++ = X25_DATA; |
} |
|
if (qbit) |
skb->data[0] |= X25_Q_BIT; |
} |
|
SOCK_DEBUG(sk, "x25_sendmsg: Built header.\n"); |
SOCK_DEBUG(sk, "x25_sendmsg: Transmitting buffer\n"); |
|
if (sk->state != TCP_ESTABLISHED) { |
kfree_skb(skb); |
return -ENOTCONN; |
} |
|
if (msg->msg_flags & MSG_OOB) { |
skb_queue_tail(&sk->protinfo.x25->interrupt_out_queue, skb); |
} else { |
len = x25_output(sk, skb); |
if(len<0){ |
kfree_skb(skb); |
} else { |
if(sk->protinfo.x25->qbitincl) len++; |
} |
} |
|
/* |
* lock_sock() is currently only used to serialize this x25_kick() |
* against input-driven x25_kick() calls. It currently only blocks |
* incoming packets for this socket and does not protect against |
* any other socket state changes and is not called from anywhere |
* else. As x25_kick() cannot block and as long as all socket |
* operations are BKL-wrapped, we don't need take to care about |
* purging the backlog queue in x25_release(). |
* |
* Using lock_sock() to protect all socket operations entirely |
* (and making the whole x25 stack SMP aware) unfortunately would |
* require major changes to {send,recv}msg and skb allocation methods. |
* -> 2.5 ;) |
*/ |
lock_sock(sk); |
x25_kick(sk); |
release_sock(sk); |
|
return len; |
} |
|
|
static int x25_recvmsg(struct socket *sock, struct msghdr *msg, int size, int flags, struct scm_cookie *scm) |
{ |
struct sock *sk = sock->sk; |
struct sockaddr_x25 *sx25 = (struct sockaddr_x25 *)msg->msg_name; |
int copied, qbit; |
struct sk_buff *skb; |
unsigned char *asmptr; |
int er; |
|
/* |
* This works for seqpacket too. The receiver has ordered the queue for |
* us! We do one quick check first though |
*/ |
if (sk->state != TCP_ESTABLISHED) |
return -ENOTCONN; |
|
if (flags & MSG_OOB) { |
if (sk->urginline || skb_peek(&sk->protinfo.x25->interrupt_in_queue) == NULL) |
return -EINVAL; |
|
skb = skb_dequeue(&sk->protinfo.x25->interrupt_in_queue); |
|
skb_pull(skb, X25_STD_MIN_LEN); |
|
/* |
* No Q bit information on Interrupt data. |
*/ |
if (sk->protinfo.x25->qbitincl) { |
asmptr = skb_push(skb, 1); |
*asmptr = 0x00; |
} |
|
msg->msg_flags |= MSG_OOB; |
} else { |
/* Now we can treat all alike */ |
if ((skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &er)) == NULL) |
return er; |
|
qbit = (skb->data[0] & X25_Q_BIT) == X25_Q_BIT; |
|
skb_pull(skb, (sk->protinfo.x25->neighbour->extended) ? X25_EXT_MIN_LEN : X25_STD_MIN_LEN); |
|
if (sk->protinfo.x25->qbitincl) { |
asmptr = skb_push(skb, 1); |
*asmptr = qbit; |
} |
} |
|
skb->h.raw = skb->data; |
|
copied = skb->len; |
|
if (copied > size) { |
copied = size; |
msg->msg_flags |= MSG_TRUNC; |
} |
|
/* Currently, each datagram always contains a complete record */ |
msg->msg_flags |= MSG_EOR; |
|
skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); |
|
if (sx25 != NULL) { |
sx25->sx25_family = AF_X25; |
sx25->sx25_addr = sk->protinfo.x25->dest_addr; |
} |
|
msg->msg_namelen = sizeof(struct sockaddr_x25); |
|
skb_free_datagram(sk, skb); |
lock_sock(sk); |
x25_check_rbuf(sk); |
release_sock(sk); |
|
return copied; |
} |
|
|
static int x25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) |
{ |
struct sock *sk = sock->sk; |
|
switch (cmd) { |
case TIOCOUTQ: { |
int amount; |
amount = sk->sndbuf - atomic_read(&sk->wmem_alloc); |
if (amount < 0) |
amount = 0; |
return put_user(amount, (unsigned int *)arg); |
} |
|
case TIOCINQ: { |
struct sk_buff *skb; |
int amount = 0; |
/* These two are safe on a single CPU system as only user tasks fiddle here */ |
if ((skb = skb_peek(&sk->receive_queue)) != NULL) |
amount = skb->len; |
return put_user(amount, (unsigned int *)arg); |
} |
|
case SIOCGSTAMP: |
if (sk != NULL) { |
if (sk->stamp.tv_sec == 0) |
return -ENOENT; |
return copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval)) ? -EFAULT : 0; |
} |
return -EINVAL; |
|
case SIOCGIFADDR: |
case SIOCSIFADDR: |
case SIOCGIFDSTADDR: |
case SIOCSIFDSTADDR: |
case SIOCGIFBRDADDR: |
case SIOCSIFBRDADDR: |
case SIOCGIFNETMASK: |
case SIOCSIFNETMASK: |
case SIOCGIFMETRIC: |
case SIOCSIFMETRIC: |
return -EINVAL; |
|
case SIOCADDRT: |
case SIOCDELRT: |
if (!capable(CAP_NET_ADMIN)) return -EPERM; |
return x25_route_ioctl(cmd, (void *)arg); |
|
case SIOCX25GSUBSCRIP: |
return x25_subscr_ioctl(cmd, (void *)arg); |
|
case SIOCX25SSUBSCRIP: |
if (!capable(CAP_NET_ADMIN)) return -EPERM; |
return x25_subscr_ioctl(cmd, (void *)arg); |
|
case SIOCX25GFACILITIES: { |
struct x25_facilities facilities; |
facilities = sk->protinfo.x25->facilities; |
return copy_to_user((void *)arg, &facilities, sizeof(facilities)) ? -EFAULT : 0; |
} |
|
case SIOCX25SFACILITIES: { |
struct x25_facilities facilities; |
if (copy_from_user(&facilities, (void *)arg, sizeof(facilities))) |
return -EFAULT; |
if (sk->state != TCP_LISTEN && sk->state != TCP_CLOSE) |
return -EINVAL; |
if (facilities.pacsize_in < X25_PS16 || facilities.pacsize_in > X25_PS4096) |
return -EINVAL; |
if (facilities.pacsize_out < X25_PS16 || facilities.pacsize_out > X25_PS4096) |
return -EINVAL; |
if (facilities.winsize_in < 1 || facilities.winsize_in > 127) |
return -EINVAL; |
if (facilities.throughput < 0x03 || facilities.throughput > 0xDD) |
return -EINVAL; |
if (facilities.reverse != 0 && facilities.reverse != 1) |
return -EINVAL; |
sk->protinfo.x25->facilities = facilities; |
return 0; |
} |
|
case SIOCX25GCALLUSERDATA: { |
struct x25_calluserdata calluserdata; |
calluserdata = sk->protinfo.x25->calluserdata; |
return copy_to_user((void *)arg, &calluserdata, sizeof(calluserdata)) ? -EFAULT : 0; |
} |
|
case SIOCX25SCALLUSERDATA: { |
struct x25_calluserdata calluserdata; |
if (copy_from_user(&calluserdata, (void *)arg, sizeof(calluserdata))) |
return -EFAULT; |
if (calluserdata.cudlength > X25_MAX_CUD_LEN) |
return -EINVAL; |
sk->protinfo.x25->calluserdata = calluserdata; |
return 0; |
} |
|
case SIOCX25GCAUSEDIAG: { |
struct x25_causediag causediag; |
causediag = sk->protinfo.x25->causediag; |
return copy_to_user((void *)arg, &causediag, sizeof(causediag)) ? -EFAULT : 0; |
} |
|
default: |
return dev_ioctl(cmd, (void *)arg); |
} |
|
/*NOTREACHED*/ |
return 0; |
} |
|
static int x25_get_info(char *buffer, char **start, off_t offset, int length) |
{ |
struct sock *s; |
struct net_device *dev; |
const char *devname; |
int len = 0; |
off_t pos = 0; |
off_t begin = 0; |
|
cli(); |
|
len += sprintf(buffer, "dest_addr src_addr dev lci st vs vr va t t2 t21 t22 t23 Snd-Q Rcv-Q inode\n"); |
|
for (s = x25_list; s != NULL; s = s->next) { |
if (s->protinfo.x25->neighbour == NULL || (dev = s->protinfo.x25->neighbour->dev) == NULL) |
devname = "???"; |
else |
devname = s->protinfo.x25->neighbour->dev->name; |
|
len += sprintf(buffer + len, "%-10s %-10s %-5s %3.3X %d %d %d %d %3lu %3lu %3lu %3lu %3lu %5d %5d %ld\n", |
(s->protinfo.x25->dest_addr.x25_addr[0] == '\0') ? "*" : s->protinfo.x25->dest_addr.x25_addr, |
(s->protinfo.x25->source_addr.x25_addr[0] == '\0') ? "*" : s->protinfo.x25->source_addr.x25_addr, |
devname, |
s->protinfo.x25->lci & 0x0FFF, |
s->protinfo.x25->state, |
s->protinfo.x25->vs, |
s->protinfo.x25->vr, |
s->protinfo.x25->va, |
x25_display_timer(s) / HZ, |
s->protinfo.x25->t2 / HZ, |
s->protinfo.x25->t21 / HZ, |
s->protinfo.x25->t22 / HZ, |
s->protinfo.x25->t23 / HZ, |
atomic_read(&s->wmem_alloc), |
atomic_read(&s->rmem_alloc), |
s->socket != NULL ? s->socket->inode->i_ino : 0L); |
|
pos = begin + len; |
|
if (pos < offset) { |
len = 0; |
begin = pos; |
} |
|
if (pos > offset + length) |
break; |
} |
|
sti(); |
|
*start = buffer + (offset - begin); |
len -= (offset - begin); |
|
if (len > length) len = length; |
|
return(len); |
} |
|
struct net_proto_family x25_family_ops = { |
family: AF_X25, |
create: x25_create, |
}; |
|
static struct proto_ops SOCKOPS_WRAPPED(x25_proto_ops) = { |
family: AF_X25, |
|
release: x25_release, |
bind: x25_bind, |
connect: x25_connect, |
socketpair: sock_no_socketpair, |
accept: x25_accept, |
getname: x25_getname, |
poll: datagram_poll, |
ioctl: x25_ioctl, |
listen: x25_listen, |
shutdown: sock_no_shutdown, |
setsockopt: x25_setsockopt, |
getsockopt: x25_getsockopt, |
sendmsg: x25_sendmsg, |
recvmsg: x25_recvmsg, |
mmap: sock_no_mmap, |
sendpage: sock_no_sendpage, |
}; |
|
#include <linux/smp_lock.h> |
SOCKOPS_WRAP(x25_proto, AF_X25); |
|
|
static struct packet_type x25_packet_type = { |
type: __constant_htons(ETH_P_X25), |
func: x25_lapb_receive_frame, |
}; |
|
struct notifier_block x25_dev_notifier = { |
notifier_call: x25_device_event, |
}; |
|
void x25_kill_by_neigh(struct x25_neigh *neigh) |
{ |
struct sock *s; |
|
for( s=x25_list; s != NULL; s=s->next){ |
if( s->protinfo.x25->neighbour == neigh ) |
x25_disconnect(s, ENETUNREACH, 0, 0); |
} |
} |
|
static int __init x25_init(void) |
{ |
#ifdef MODULE |
struct net_device *dev; |
#endif /* MODULE */ |
sock_register(&x25_family_ops); |
|
dev_add_pack(&x25_packet_type); |
|
register_netdevice_notifier(&x25_dev_notifier); |
|
printk(KERN_INFO "X.25 for Linux. Version 0.2 for Linux 2.1.15\n"); |
|
#ifdef CONFIG_SYSCTL |
x25_register_sysctl(); |
#endif |
|
proc_net_create("x25", 0, x25_get_info); |
proc_net_create("x25_routes", 0, x25_routes_get_info); |
|
#ifdef MODULE |
/* |
* Register any pre existing devices. |
*/ |
read_lock(&dev_base_lock); |
for (dev = dev_base; dev != NULL; dev = dev->next) { |
if ((dev->flags & IFF_UP) && (dev->type == ARPHRD_X25 |
#if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE) |
|| dev->type == ARPHRD_ETHER |
#endif |
)) |
x25_link_device_up(dev); |
} |
read_unlock(&dev_base_lock); |
#endif /* MODULE */ |
return 0; |
} |
module_init(x25_init); |
|
|
|
EXPORT_NO_SYMBOLS; |
|
MODULE_AUTHOR("Jonathan Naylor <g4klx@g4klx.demon.co.uk>"); |
MODULE_DESCRIPTION("The X.25 Packet Layer network layer protocol"); |
MODULE_LICENSE("GPL"); |
|
static void __exit x25_exit(void) |
{ |
|
proc_net_remove("x25"); |
proc_net_remove("x25_routes"); |
|
x25_link_free(); |
x25_route_free(); |
|
#ifdef CONFIG_SYSCTL |
x25_unregister_sysctl(); |
#endif |
|
unregister_netdevice_notifier(&x25_dev_notifier); |
|
dev_remove_pack(&x25_packet_type); |
|
sock_unregister(AF_X25); |
} |
module_exit(x25_exit); |
|
/x25_out.c
0,0 → 1,229
/* |
* X.25 Packet Layer release 002 |
* |
* 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. |
* |
* This code REQUIRES 2.1.15 or higher |
* |
* This module: |
* This module is free software; you can redistribute it and/or |
* modify it under the terms of the GNU General Public License |
* as published by the Free Software Foundation; either version |
* 2 of the License, or (at your option) any later version. |
* |
* History |
* X.25 001 Jonathan Naylor Started coding. |
* X.25 002 Jonathan Naylor New timer architecture. |
* 2000-09-04 Henner Eisen Prevented x25_output() skb leakage. |
* 2000-10-27 Henner Eisen MSG_DONTWAIT for fragment allocation. |
* 2000-11-10 Henner Eisen x25_send_iframe(): re-queued frames |
* needed cleaned seq-number fields. |
*/ |
|
#include <linux/errno.h> |
#include <linux/types.h> |
#include <linux/socket.h> |
#include <linux/in.h> |
#include <linux/kernel.h> |
#include <linux/sched.h> |
#include <linux/timer.h> |
#include <linux/string.h> |
#include <linux/sockios.h> |
#include <linux/net.h> |
#include <linux/inet.h> |
#include <linux/netdevice.h> |
#include <linux/skbuff.h> |
#include <net/sock.h> |
#include <asm/segment.h> |
#include <asm/system.h> |
#include <linux/fcntl.h> |
#include <linux/mm.h> |
#include <linux/interrupt.h> |
#include <net/x25.h> |
|
static int x25_pacsize_to_bytes(unsigned int pacsize) |
{ |
int bytes = 1; |
|
if (pacsize == 0) |
return 128; |
|
while (pacsize-- > 0) |
bytes *= 2; |
|
return bytes; |
} |
|
/* |
* This is where all X.25 information frames pass. |
* |
* Returns the amount of user data bytes sent on success |
* or a negative error code on failure. |
*/ |
int x25_output(struct sock *sk, struct sk_buff *skb) |
{ |
struct sk_buff *skbn; |
unsigned char header[X25_EXT_MIN_LEN]; |
int err, frontlen, len, header_len, max_len; |
int sent=0, noblock = X25_SKB_CB(skb)->flags & MSG_DONTWAIT; |
|
header_len = (sk->protinfo.x25->neighbour->extended) ? X25_EXT_MIN_LEN : X25_STD_MIN_LEN; |
max_len = x25_pacsize_to_bytes(sk->protinfo.x25->facilities.pacsize_out); |
|
if (skb->len - header_len > max_len) { |
/* Save a copy of the Header */ |
memcpy(header, skb->data, header_len); |
skb_pull(skb, header_len); |
|
frontlen = skb_headroom(skb); |
|
while (skb->len > 0) { |
if ((skbn = sock_alloc_send_skb(sk, frontlen + max_len, noblock, &err)) == NULL){ |
if(err == -EWOULDBLOCK && noblock){ |
kfree_skb(skb); |
return sent; |
} |
SOCK_DEBUG(sk, "x25_output: fragment allocation failed, err=%d, %d bytes sent\n", err, sent); |
return err; |
} |
|
skb_reserve(skbn, frontlen); |
|
len = (max_len > skb->len) ? skb->len : max_len; |
|
/* Copy the user data */ |
memcpy(skb_put(skbn, len), skb->data, len); |
skb_pull(skb, len); |
|
/* Duplicate the Header */ |
skb_push(skbn, header_len); |
memcpy(skbn->data, header, header_len); |
|
if (skb->len > 0) { |
if (sk->protinfo.x25->neighbour->extended) |
skbn->data[3] |= X25_EXT_M_BIT; |
else |
skbn->data[2] |= X25_STD_M_BIT; |
} |
|
skb_queue_tail(&sk->write_queue, skbn); |
sent += len; |
} |
|
kfree_skb(skb); |
} else { |
skb_queue_tail(&sk->write_queue, skb); |
sent = skb->len - header_len; |
} |
return sent; |
} |
|
/* |
* This procedure is passed a buffer descriptor for an iframe. It builds |
* the rest of the control part of the frame and then writes it out. |
*/ |
static void x25_send_iframe(struct sock *sk, struct sk_buff *skb) |
{ |
if (skb == NULL) |
return; |
|
if (sk->protinfo.x25->neighbour->extended) { |
skb->data[2] = (sk->protinfo.x25->vs << 1) & 0xFE; |
skb->data[3] &= X25_EXT_M_BIT; |
skb->data[3] |= (sk->protinfo.x25->vr << 1) & 0xFE; |
} else { |
skb->data[2] &= X25_STD_M_BIT; |
skb->data[2] |= (sk->protinfo.x25->vs << 1) & 0x0E; |
skb->data[2] |= (sk->protinfo.x25->vr << 5) & 0xE0; |
} |
|
x25_transmit_link(skb, sk->protinfo.x25->neighbour); |
} |
|
void x25_kick(struct sock *sk) |
{ |
struct sk_buff *skb, *skbn; |
unsigned short start, end; |
int modulus; |
|
if (sk->protinfo.x25->state != X25_STATE_3) |
return; |
|
/* |
* Transmit interrupt data. |
*/ |
if (!sk->protinfo.x25->intflag && skb_peek(&sk->protinfo.x25->interrupt_out_queue) != NULL) { |
sk->protinfo.x25->intflag = 1; |
skb = skb_dequeue(&sk->protinfo.x25->interrupt_out_queue); |
x25_transmit_link(skb, sk->protinfo.x25->neighbour); |
} |
|
if (sk->protinfo.x25->condition & X25_COND_PEER_RX_BUSY) |
return; |
|
if (skb_peek(&sk->write_queue) == NULL) |
return; |
|
modulus = (sk->protinfo.x25->neighbour->extended) ? X25_EMODULUS : X25_SMODULUS; |
|
start = (skb_peek(&sk->protinfo.x25->ack_queue) == NULL) ? sk->protinfo.x25->va : sk->protinfo.x25->vs; |
end = (sk->protinfo.x25->va + sk->protinfo.x25->facilities.winsize_out) % modulus; |
|
if (start == end) |
return; |
|
sk->protinfo.x25->vs = start; |
|
/* |
* Transmit data until either we're out of data to send or |
* the window is full. |
*/ |
|
skb = skb_dequeue(&sk->write_queue); |
|
do { |
if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) { |
skb_queue_head(&sk->write_queue, skb); |
break; |
} |
|
skb_set_owner_w(skbn, sk); |
|
/* |
* Transmit the frame copy. |
*/ |
x25_send_iframe(sk, skbn); |
|
sk->protinfo.x25->vs = (sk->protinfo.x25->vs + 1) % modulus; |
|
/* |
* Requeue the original data frame. |
*/ |
skb_queue_tail(&sk->protinfo.x25->ack_queue, skb); |
|
} while (sk->protinfo.x25->vs != end && (skb = skb_dequeue(&sk->write_queue)) != NULL); |
|
sk->protinfo.x25->vl = sk->protinfo.x25->vr; |
sk->protinfo.x25->condition &= ~X25_COND_ACK_PENDING; |
|
x25_stop_timer(sk); |
} |
|
/* |
* The following routines are taken from page 170 of the 7th ARRL Computer |
* Networking Conference paper, as is the whole state machine. |
*/ |
|
void x25_enquiry_response(struct sock *sk) |
{ |
if (sk->protinfo.x25->condition & X25_COND_OWN_RX_BUSY) |
x25_write_internal(sk, X25_RNR); |
else |
x25_write_internal(sk, X25_RR); |
|
sk->protinfo.x25->vl = sk->protinfo.x25->vr; |
sk->protinfo.x25->condition &= ~X25_COND_ACK_PENDING; |
|
x25_stop_timer(sk); |
} |
/sysctl_net_x25.c
0,0 → 1,58
/* -*- linux-c -*- |
* sysctl_net_x25.c: sysctl interface to net X.25 subsystem. |
* |
* Begun April 1, 1996, Mike Shaver. |
* Added /proc/sys/net/x25 directory entry (empty =) ). [MS] |
*/ |
|
#include <linux/mm.h> |
#include <linux/sysctl.h> |
#include <linux/skbuff.h> |
#include <linux/socket.h> |
#include <linux/netdevice.h> |
#include <linux/init.h> |
#include <net/x25.h> |
|
static int min_timer[] = {1 * HZ}; |
static int max_timer[] = {300 * HZ}; |
|
static struct ctl_table_header *x25_table_header; |
|
static ctl_table x25_table[] = { |
{NET_X25_RESTART_REQUEST_TIMEOUT, "restart_request_timeout", |
&sysctl_x25_restart_request_timeout, sizeof(int), 0644, NULL, |
&proc_dointvec_minmax, &sysctl_intvec, NULL, &min_timer, &max_timer}, |
{NET_X25_CALL_REQUEST_TIMEOUT, "call_request_timeout", |
&sysctl_x25_call_request_timeout, sizeof(int), 0644, NULL, |
&proc_dointvec_minmax, &sysctl_intvec, NULL, &min_timer, &max_timer}, |
{NET_X25_RESET_REQUEST_TIMEOUT, "reset_request_timeout", |
&sysctl_x25_reset_request_timeout, sizeof(int), 0644, NULL, |
&proc_dointvec_minmax, &sysctl_intvec, NULL, &min_timer, &max_timer}, |
{NET_X25_CLEAR_REQUEST_TIMEOUT, "clear_request_timeout", |
&sysctl_x25_clear_request_timeout, sizeof(int), 0644, NULL, |
&proc_dointvec_minmax, &sysctl_intvec, NULL, &min_timer, &max_timer}, |
{NET_X25_ACK_HOLD_BACK_TIMEOUT, "acknowledgement_hold_back_timeout", |
&sysctl_x25_ack_holdback_timeout, sizeof(int), 0644, NULL, |
&proc_dointvec_minmax, &sysctl_intvec, NULL, &min_timer, &max_timer}, |
{0} |
}; |
|
static ctl_table x25_dir_table[] = { |
{NET_X25, "x25", NULL, 0, 0555, x25_table}, |
{0} |
}; |
|
static ctl_table x25_root_table[] = { |
{CTL_NET, "net", NULL, 0, 0555, x25_dir_table}, |
{0} |
}; |
|
void __init x25_register_sysctl(void) |
{ |
x25_table_header = register_sysctl_table(x25_root_table, 1); |
} |
|
void x25_unregister_sysctl(void) |
{ |
unregister_sysctl_table(x25_table_header); |
} |
/x25_subr.c
0,0 → 1,356
/* |
* X.25 Packet Layer release 002 |
* |
* 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. |
* |
* This code REQUIRES 2.1.15 or higher |
* |
* This module: |
* This module is free software; you can redistribute it and/or |
* modify it under the terms of the GNU General Public License |
* as published by the Free Software Foundation; either version |
* 2 of the License, or (at your option) any later version. |
* |
* History |
* X.25 001 Jonathan Naylor Started coding. |
* X.25 002 Jonathan Naylor Centralised disconnection processing. |
* mar/20/00 Daniela Squassoni Disabling/enabling of facilities |
* negotiation. |
* jun/24/01 Arnaldo C. Melo use skb_queue_purge, cleanups |
*/ |
|
#include <linux/errno.h> |
#include <linux/types.h> |
#include <linux/socket.h> |
#include <linux/in.h> |
#include <linux/kernel.h> |
#include <linux/sched.h> |
#include <linux/timer.h> |
#include <linux/string.h> |
#include <linux/sockios.h> |
#include <linux/net.h> |
#include <linux/inet.h> |
#include <linux/netdevice.h> |
#include <linux/skbuff.h> |
#include <net/sock.h> |
#include <asm/segment.h> |
#include <asm/system.h> |
#include <linux/fcntl.h> |
#include <linux/mm.h> |
#include <linux/interrupt.h> |
#include <net/x25.h> |
|
/* |
* This routine purges all of the queues of frames. |
*/ |
void x25_clear_queues(struct sock *sk) |
{ |
skb_queue_purge(&sk->write_queue); |
skb_queue_purge(&sk->protinfo.x25->ack_queue); |
skb_queue_purge(&sk->protinfo.x25->interrupt_in_queue); |
skb_queue_purge(&sk->protinfo.x25->interrupt_out_queue); |
skb_queue_purge(&sk->protinfo.x25->fragment_queue); |
} |
|
|
/* |
* This routine purges the input queue of those frames that have been |
* acknowledged. This replaces the boxes labelled "V(a) <- N(r)" on the |
* SDL diagram. |
*/ |
void x25_frames_acked(struct sock *sk, unsigned short nr) |
{ |
struct sk_buff *skb; |
int modulus = sk->protinfo.x25->neighbour->extended ? X25_EMODULUS : |
X25_SMODULUS; |
|
/* |
* Remove all the ack-ed frames from the ack queue. |
*/ |
if (sk->protinfo.x25->va != nr) |
while (skb_peek(&sk->protinfo.x25->ack_queue) != NULL && |
sk->protinfo.x25->va != nr) { |
skb = skb_dequeue(&sk->protinfo.x25->ack_queue); |
kfree_skb(skb); |
sk->protinfo.x25->va = (sk->protinfo.x25->va + 1) % |
modulus; |
} |
} |
|
void x25_requeue_frames(struct sock *sk) |
{ |
struct sk_buff *skb, *skb_prev = NULL; |
|
/* |
* Requeue all the un-ack-ed frames on the output queue to be picked |
* up by x25_kick. This arrangement handles the possibility of an empty |
* output queue. |
*/ |
while ((skb = skb_dequeue(&sk->protinfo.x25->ack_queue)) != NULL) { |
if (skb_prev == NULL) |
skb_queue_head(&sk->write_queue, skb); |
else |
skb_append(skb_prev, skb); |
skb_prev = skb; |
} |
} |
|
/* |
* Validate that the value of nr is between va and vs. Return true or |
* false for testing. |
*/ |
int x25_validate_nr(struct sock *sk, unsigned short nr) |
{ |
unsigned short vc = sk->protinfo.x25->va; |
int modulus = sk->protinfo.x25->neighbour->extended ? X25_EMODULUS : |
X25_SMODULUS; |
|
while (vc != sk->protinfo.x25->vs) { |
if (nr == vc) return 1; |
vc = (vc + 1) % modulus; |
} |
|
return nr == sk->protinfo.x25->vs ? 1 : 0; |
} |
|
/* |
* This routine is called when the packet layer internally generates a |
* control frame. |
*/ |
void x25_write_internal(struct sock *sk, int frametype) |
{ |
struct sk_buff *skb; |
unsigned char *dptr; |
unsigned char facilities[X25_MAX_FAC_LEN]; |
unsigned char addresses[1 + X25_ADDR_LEN]; |
unsigned char lci1, lci2; |
int len; |
|
/* |
* Default safe frame size. |
*/ |
len = X25_MAX_L2_LEN + X25_EXT_MIN_LEN; |
|
/* |
* Adjust frame size. |
*/ |
switch (frametype) { |
case X25_CALL_REQUEST: |
len += 1 + X25_ADDR_LEN + X25_MAX_FAC_LEN + |
X25_MAX_CUD_LEN; |
break; |
case X25_CALL_ACCEPTED: |
len += 1 + X25_MAX_FAC_LEN + X25_MAX_CUD_LEN; |
break; |
case X25_CLEAR_REQUEST: |
case X25_RESET_REQUEST: |
len += 2; |
break; |
case X25_RR: |
case X25_RNR: |
case X25_REJ: |
case X25_CLEAR_CONFIRMATION: |
case X25_INTERRUPT_CONFIRMATION: |
case X25_RESET_CONFIRMATION: |
break; |
default: |
printk(KERN_ERR "X.25: invalid frame type %02X\n", |
frametype); |
return; |
} |
|
if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL) |
return; |
|
/* |
* Space for Ethernet and 802.2 LLC headers. |
*/ |
skb_reserve(skb, X25_MAX_L2_LEN); |
|
/* |
* Make space for the GFI and LCI, and fill them in. |
*/ |
dptr = skb_put(skb, 2); |
|
lci1 = (sk->protinfo.x25->lci >> 8) & 0x0F; |
lci2 = (sk->protinfo.x25->lci >> 0) & 0xFF; |
|
if (sk->protinfo.x25->neighbour->extended) { |
*dptr++ = lci1 | X25_GFI_EXTSEQ; |
*dptr++ = lci2; |
} else { |
*dptr++ = lci1 | X25_GFI_STDSEQ; |
*dptr++ = lci2; |
} |
|
/* |
* Now fill in the frame type specific information. |
*/ |
switch (frametype) { |
|
case X25_CALL_REQUEST: |
dptr = skb_put(skb, 1); |
*dptr++ = X25_CALL_REQUEST; |
len = x25_addr_aton(addresses, &sk->protinfo.x25->dest_addr, &sk->protinfo.x25->source_addr); |
dptr = skb_put(skb, len); |
memcpy(dptr, addresses, len); |
len = x25_create_facilities(facilities, &sk->protinfo.x25->facilities, sk->protinfo.x25->neighbour->global_facil_mask); |
dptr = skb_put(skb, len); |
memcpy(dptr, facilities, len); |
dptr = skb_put(skb, sk->protinfo.x25->calluserdata.cudlength); |
memcpy(dptr, sk->protinfo.x25->calluserdata.cuddata, sk->protinfo.x25->calluserdata.cudlength); |
sk->protinfo.x25->calluserdata.cudlength = 0; |
break; |
|
case X25_CALL_ACCEPTED: |
dptr = skb_put(skb, 2); |
*dptr++ = X25_CALL_ACCEPTED; |
*dptr++ = 0x00; /* Address lengths */ |
len = x25_create_facilities(facilities, &sk->protinfo.x25->facilities, sk->protinfo.x25->vc_facil_mask); |
dptr = skb_put(skb, len); |
memcpy(dptr, facilities, len); |
dptr = skb_put(skb, sk->protinfo.x25->calluserdata.cudlength); |
memcpy(dptr, sk->protinfo.x25->calluserdata.cuddata, sk->protinfo.x25->calluserdata.cudlength); |
sk->protinfo.x25->calluserdata.cudlength = 0; |
break; |
|
case X25_CLEAR_REQUEST: |
case X25_RESET_REQUEST: |
dptr = skb_put(skb, 3); |
*dptr++ = frametype; |
*dptr++ = 0x00; /* XXX */ |
*dptr++ = 0x00; /* XXX */ |
break; |
|
case X25_RR: |
case X25_RNR: |
case X25_REJ: |
if (sk->protinfo.x25->neighbour->extended) { |
dptr = skb_put(skb, 2); |
*dptr++ = frametype; |
*dptr++ = (sk->protinfo.x25->vr << 1) & 0xFE; |
} else { |
dptr = skb_put(skb, 1); |
*dptr = frametype; |
*dptr++ |= (sk->protinfo.x25->vr << 5) & 0xE0; |
} |
break; |
|
case X25_CLEAR_CONFIRMATION: |
case X25_INTERRUPT_CONFIRMATION: |
case X25_RESET_CONFIRMATION: |
dptr = skb_put(skb, 1); |
*dptr = frametype; |
break; |
} |
|
x25_transmit_link(skb, sk->protinfo.x25->neighbour); |
} |
|
/* |
* Unpick the contents of the passed X.25 Packet Layer frame. |
*/ |
int x25_decode(struct sock *sk, struct sk_buff *skb, int *ns, int *nr, int *q, |
int *d, int *m) |
{ |
unsigned char *frame = skb->data; |
|
*ns = *nr = *q = *d = *m = 0; |
|
switch (frame[2]) { |
case X25_CALL_REQUEST: |
case X25_CALL_ACCEPTED: |
case X25_CLEAR_REQUEST: |
case X25_CLEAR_CONFIRMATION: |
case X25_INTERRUPT: |
case X25_INTERRUPT_CONFIRMATION: |
case X25_RESET_REQUEST: |
case X25_RESET_CONFIRMATION: |
case X25_RESTART_REQUEST: |
case X25_RESTART_CONFIRMATION: |
case X25_REGISTRATION_REQUEST: |
case X25_REGISTRATION_CONFIRMATION: |
case X25_DIAGNOSTIC: |
return frame[2]; |
} |
|
if (sk->protinfo.x25->neighbour->extended) { |
if (frame[2] == X25_RR || |
frame[2] == X25_RNR || |
frame[2] == X25_REJ) { |
*nr = (frame[3] >> 1) & 0x7F; |
return frame[2]; |
} |
} else { |
if ((frame[2] & 0x1F) == X25_RR || |
(frame[2] & 0x1F) == X25_RNR || |
(frame[2] & 0x1F) == X25_REJ) { |
*nr = (frame[2] >> 5) & 0x07; |
return frame[2] & 0x1F; |
} |
} |
|
if (sk->protinfo.x25->neighbour->extended) { |
if ((frame[2] & 0x01) == X25_DATA) { |
*q = (frame[0] & X25_Q_BIT) == X25_Q_BIT; |
*d = (frame[0] & X25_D_BIT) == X25_D_BIT; |
*m = (frame[3] & X25_EXT_M_BIT) == X25_EXT_M_BIT; |
*nr = (frame[3] >> 1) & 0x7F; |
*ns = (frame[2] >> 1) & 0x7F; |
return X25_DATA; |
} |
} else { |
if ((frame[2] & 0x01) == X25_DATA) { |
*q = (frame[0] & X25_Q_BIT) == X25_Q_BIT; |
*d = (frame[0] & X25_D_BIT) == X25_D_BIT; |
*m = (frame[2] & X25_STD_M_BIT) == X25_STD_M_BIT; |
*nr = (frame[2] >> 5) & 0x07; |
*ns = (frame[2] >> 1) & 0x07; |
return X25_DATA; |
} |
} |
|
printk(KERN_DEBUG "X.25: invalid PLP frame %02X %02X %02X\n", |
frame[0], frame[1], frame[2]); |
|
return X25_ILLEGAL; |
} |
|
void x25_disconnect(struct sock *sk, int reason, unsigned char cause, |
unsigned char diagnostic) |
{ |
x25_clear_queues(sk); |
x25_stop_timer(sk); |
|
sk->protinfo.x25->lci = 0; |
sk->protinfo.x25->state = X25_STATE_0; |
|
sk->protinfo.x25->causediag.cause = cause; |
sk->protinfo.x25->causediag.diagnostic = diagnostic; |
|
sk->state = TCP_CLOSE; |
sk->err = reason; |
sk->shutdown |= SEND_SHUTDOWN; |
|
if (!sk->dead) |
sk->state_change(sk); |
|
sk->dead = 1; |
} |
|
/* |
* Clear an own-rx-busy condition and tell the peer about this, provided |
* that there is a significant amount of free receive buffer space available. |
*/ |
void x25_check_rbuf(struct sock *sk) |
{ |
if (atomic_read(&sk->rmem_alloc) < (sk->rcvbuf / 2) && |
(sk->protinfo.x25->condition & X25_COND_OWN_RX_BUSY)) { |
sk->protinfo.x25->condition &= ~X25_COND_OWN_RX_BUSY; |
sk->protinfo.x25->condition &= ~X25_COND_ACK_PENDING; |
sk->protinfo.x25->vl = sk->protinfo.x25->vr; |
x25_write_internal(sk, X25_RR); |
x25_stop_timer(sk); |
} |
} |
/x25_facilities.c
0,0 → 1,232
/* |
* X.25 Packet Layer release 002 |
* |
* 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. |
* |
* This code REQUIRES 2.1.15 or higher |
* |
* This module: |
* This module is free software; you can redistribute it and/or |
* modify it under the terms of the GNU General Public License |
* as published by the Free Software Foundation; either version |
* 2 of the License, or (at your option) any later version. |
* |
* History |
* X.25 001 Split from x25_subr.c |
* mar/20/00 Daniela Squassoni Disabling/enabling of facilities |
* negotiation. |
*/ |
|
#include <linux/errno.h> |
#include <linux/types.h> |
#include <linux/socket.h> |
#include <linux/in.h> |
#include <linux/kernel.h> |
#include <linux/sched.h> |
#include <linux/timer.h> |
#include <linux/string.h> |
#include <linux/sockios.h> |
#include <linux/net.h> |
#include <linux/inet.h> |
#include <linux/netdevice.h> |
#include <linux/skbuff.h> |
#include <net/sock.h> |
#include <asm/segment.h> |
#include <asm/system.h> |
#include <linux/fcntl.h> |
#include <linux/mm.h> |
#include <linux/interrupt.h> |
#include <net/x25.h> |
|
/* |
* Parse a set of facilities into the facilities structure. Unrecognised |
* facilities are written to the debug log file. |
*/ |
int x25_parse_facilities(struct sk_buff *skb, struct x25_facilities *facilities, unsigned long *vc_fac_mask) |
{ |
unsigned int len; |
unsigned char *p = skb->data; |
|
len = *p++; |
*vc_fac_mask = 0; |
|
while (len > 0) { |
switch (*p & X25_FAC_CLASS_MASK) { |
case X25_FAC_CLASS_A: |
switch (*p) { |
case X25_FAC_REVERSE: |
facilities->reverse = (p[1] & 0x01); |
*vc_fac_mask |= X25_MASK_REVERSE; |
break; |
case X25_FAC_THROUGHPUT: |
facilities->throughput = p[1]; |
*vc_fac_mask |= X25_MASK_THROUGHPUT; |
break; |
default: |
printk(KERN_DEBUG "X.25: unknown facility %02X, value %02X\n", p[0], p[1]); |
break; |
} |
p += 2; |
len -= 2; |
break; |
|
case X25_FAC_CLASS_B: |
switch (*p) { |
case X25_FAC_PACKET_SIZE: |
facilities->pacsize_in = p[1]; |
facilities->pacsize_out = p[2]; |
*vc_fac_mask |= X25_MASK_PACKET_SIZE; |
break; |
case X25_FAC_WINDOW_SIZE: |
facilities->winsize_in = p[1]; |
facilities->winsize_out = p[2]; |
*vc_fac_mask |= X25_MASK_WINDOW_SIZE; |
break; |
default: |
printk(KERN_DEBUG "X.25: unknown facility %02X, values %02X, %02X\n", p[0], p[1], p[2]); |
break; |
} |
p += 3; |
len -= 3; |
break; |
|
case X25_FAC_CLASS_C: |
printk(KERN_DEBUG "X.25: unknown facility %02X, values %02X, %02X, %02X\n", p[0], p[1], p[2], p[3]); |
p += 4; |
len -= 4; |
break; |
|
case X25_FAC_CLASS_D: |
printk(KERN_DEBUG "X.25: unknown facility %02X, length %d, values %02X, %02X, %02X, %02X\n", p[0], p[1], p[2], p[3], p[4], p[5]); |
len -= p[1] + 2; |
p += p[1] + 2; |
break; |
} |
} |
|
return p - skb->data; |
} |
|
/* |
* Create a set of facilities. |
*/ |
int x25_create_facilities(unsigned char *buffer, struct x25_facilities *facilities, unsigned long facil_mask) |
{ |
unsigned char *p = buffer + 1; |
int len; |
|
if (facil_mask == 0) { |
buffer [0] = 0; /* length of the facilities field in call_req or call_accept packets */ |
len = 1; /* 1 byte for the length field */ |
return len; |
} |
|
if ((facilities->reverse != 0) && (facil_mask & X25_MASK_REVERSE)) { |
*p++ = X25_FAC_REVERSE; |
*p++ = (facilities->reverse) ? 0x01 : 0x00; |
} |
|
if ((facilities->throughput != 0) && (facil_mask & X25_MASK_THROUGHPUT)) { |
*p++ = X25_FAC_THROUGHPUT; |
*p++ = facilities->throughput; |
} |
|
if ((facilities->pacsize_in != 0 || facilities->pacsize_out != 0) && (facil_mask & X25_MASK_PACKET_SIZE)) { |
*p++ = X25_FAC_PACKET_SIZE; |
*p++ = (facilities->pacsize_in == 0) ? facilities->pacsize_out : facilities->pacsize_in; |
*p++ = (facilities->pacsize_out == 0) ? facilities->pacsize_in : facilities->pacsize_out; |
} |
|
if ((facilities->winsize_in != 0 || facilities->winsize_out != 0) && (facil_mask & X25_MASK_WINDOW_SIZE)) { |
*p++ = X25_FAC_WINDOW_SIZE; |
*p++ = (facilities->winsize_in == 0) ? facilities->winsize_out : facilities->winsize_in; |
*p++ = (facilities->winsize_out == 0) ? facilities->winsize_in : facilities->winsize_out; |
} |
|
len = p - buffer; |
buffer[0] = len - 1; |
|
return len; |
} |
|
/* |
* Try to reach a compromise on a set of facilities. |
* |
* The only real problem is with reverse charging. |
*/ |
int x25_negotiate_facilities(struct sk_buff *skb, struct sock *sk, struct x25_facilities *new) |
{ |
struct x25_facilities *ours; |
struct x25_facilities theirs; |
int len; |
|
memset(&theirs, 0x00, sizeof(struct x25_facilities)); |
|
ours = &sk->protinfo.x25->facilities; |
|
*new = *ours; |
|
len = x25_parse_facilities(skb, &theirs, &sk->protinfo.x25->vc_facil_mask); |
|
/* |
* They want reverse charging, we won't accept it. |
*/ |
if (theirs.reverse != 0 && ours->reverse == 0) { |
SOCK_DEBUG(sk, "X.25: rejecting reverse charging request"); |
return -1; |
} |
|
new->reverse = theirs.reverse; |
|
if (theirs.throughput != 0) { |
if (theirs.throughput < ours->throughput) { |
SOCK_DEBUG(sk, "X.25: throughput negotiated down"); |
new->throughput = theirs.throughput; |
} |
} |
|
if (theirs.pacsize_in != 0 && theirs.pacsize_out != 0) { |
if (theirs.pacsize_in < ours->pacsize_in) { |
SOCK_DEBUG(sk, "X.25: packet size inwards negotiated down"); |
new->pacsize_in = theirs.pacsize_in; |
} |
if (theirs.pacsize_out < ours->pacsize_out) { |
SOCK_DEBUG(sk, "X.25: packet size outwards negotiated down"); |
new->pacsize_out = theirs.pacsize_out; |
} |
} |
|
if (theirs.winsize_in != 0 && theirs.winsize_out != 0) { |
if (theirs.winsize_in < ours->winsize_in) { |
SOCK_DEBUG(sk, "X.25: window size inwards negotiated down"); |
new->winsize_in = theirs.winsize_in; |
} |
if (theirs.winsize_out < ours->winsize_out) { |
SOCK_DEBUG(sk, "X.25: window size outwards negotiated down"); |
new->winsize_out = theirs.winsize_out; |
} |
} |
|
return len; |
} |
|
/* |
* Limit values of certain facilities according to the capability of the |
* currently attached x25 link. |
*/ |
void x25_limit_facilities(struct x25_facilities *facilities, |
struct x25_neigh *neighbour) |
{ |
|
if( ! neighbour->extended ){ |
if( facilities->winsize_in > 7 ){ |
printk(KERN_DEBUG "X.25: incoming winsize limited to 7\n"); |
facilities->winsize_in = 7; |
} |
if( facilities->winsize_out > 7 ){ |
facilities->winsize_out = 7; |
printk( KERN_DEBUG "X.25: outgoing winsize limited to 7\n"); |
} |
} |
} |
/x25_link.c
0,0 → 1,436
/* |
* X.25 Packet Layer release 002 |
* |
* 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. |
* |
* This code REQUIRES 2.1.15 or higher |
* |
* This module: |
* This module is free software; you can redistribute it and/or |
* modify it under the terms of the GNU General Public License |
* as published by the Free Software Foundation; either version |
* 2 of the License, or (at your option) any later version. |
* |
* History |
* X.25 001 Jonathan Naylor Started coding. |
* X.25 002 Jonathan Naylor New timer architecture. |
* mar/20/00 Daniela Squassoni Disabling/enabling of facilities |
* negotiation. |
* 2000-09-04 Henner Eisen dev_hold() / dev_put() for x25_neigh. |
*/ |
|
#include <linux/errno.h> |
#include <linux/types.h> |
#include <linux/socket.h> |
#include <linux/in.h> |
#include <linux/kernel.h> |
#include <linux/sched.h> |
#include <linux/timer.h> |
#include <linux/string.h> |
#include <linux/sockios.h> |
#include <linux/net.h> |
#include <linux/inet.h> |
#include <linux/netdevice.h> |
#include <linux/skbuff.h> |
#include <net/sock.h> |
#include <asm/segment.h> |
#include <asm/system.h> |
#include <asm/uaccess.h> |
#include <linux/fcntl.h> |
#include <linux/mm.h> |
#include <linux/interrupt.h> |
#include <linux/init.h> |
#include <net/x25.h> |
|
static struct x25_neigh *x25_neigh_list /* = NULL initially */; |
|
static void x25_t20timer_expiry(unsigned long); |
|
/* |
* Linux set/reset timer routines |
*/ |
static void x25_start_t20timer(struct x25_neigh *neigh) |
{ |
del_timer(&neigh->t20timer); |
|
neigh->t20timer.data = (unsigned long)neigh; |
neigh->t20timer.function = &x25_t20timer_expiry; |
neigh->t20timer.expires = jiffies + neigh->t20; |
|
add_timer(&neigh->t20timer); |
} |
|
static void x25_t20timer_expiry(unsigned long param) |
{ |
struct x25_neigh *neigh = (struct x25_neigh *)param; |
|
x25_transmit_restart_request(neigh); |
|
x25_start_t20timer(neigh); |
} |
|
static void x25_stop_t20timer(struct x25_neigh *neigh) |
{ |
del_timer(&neigh->t20timer); |
} |
|
static int x25_t20timer_pending(struct x25_neigh *neigh) |
{ |
return timer_pending(&neigh->t20timer); |
} |
|
/* |
* This handles all restart and diagnostic frames. |
*/ |
void x25_link_control(struct sk_buff *skb, struct x25_neigh *neigh, unsigned short frametype) |
{ |
struct sk_buff *skbn; |
int confirm; |
|
switch (frametype) { |
case X25_RESTART_REQUEST: |
confirm = !x25_t20timer_pending(neigh); |
x25_stop_t20timer(neigh); |
neigh->state = X25_LINK_STATE_3; |
if (confirm) x25_transmit_restart_confirmation(neigh); |
break; |
|
case X25_RESTART_CONFIRMATION: |
x25_stop_t20timer(neigh); |
neigh->state = X25_LINK_STATE_3; |
break; |
|
case X25_DIAGNOSTIC: |
printk(KERN_WARNING "x25: diagnostic #%d - %02X %02X %02X\n", skb->data[3], skb->data[4], skb->data[5], skb->data[6]); |
break; |
|
default: |
printk(KERN_WARNING "x25: received unknown %02X with LCI 000\n", frametype); |
break; |
} |
|
if (neigh->state == X25_LINK_STATE_3) { |
while ((skbn = skb_dequeue(&neigh->queue)) != NULL) |
x25_send_frame(skbn, neigh); |
} |
} |
|
/* |
* This routine is called when a Restart Request is needed |
*/ |
void x25_transmit_restart_request(struct x25_neigh *neigh) |
{ |
struct sk_buff *skb; |
unsigned char *dptr; |
int len; |
|
len = X25_MAX_L2_LEN + X25_STD_MIN_LEN + 2; |
|
if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL) |
return; |
|
skb_reserve(skb, X25_MAX_L2_LEN); |
|
dptr = skb_put(skb, X25_STD_MIN_LEN + 2); |
|
*dptr++ = (neigh->extended) ? X25_GFI_EXTSEQ : X25_GFI_STDSEQ; |
*dptr++ = 0x00; |
*dptr++ = X25_RESTART_REQUEST; |
*dptr++ = 0x00; |
*dptr++ = 0; |
|
skb->sk = NULL; |
|
x25_send_frame(skb, neigh); |
} |
|
/* |
* This routine is called when a Restart Confirmation is needed |
*/ |
void x25_transmit_restart_confirmation(struct x25_neigh *neigh) |
{ |
struct sk_buff *skb; |
unsigned char *dptr; |
int len; |
|
len = X25_MAX_L2_LEN + X25_STD_MIN_LEN; |
|
if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL) |
return; |
|
skb_reserve(skb, X25_MAX_L2_LEN); |
|
dptr = skb_put(skb, X25_STD_MIN_LEN); |
|
*dptr++ = (neigh->extended) ? X25_GFI_EXTSEQ : X25_GFI_STDSEQ; |
*dptr++ = 0x00; |
*dptr++ = X25_RESTART_CONFIRMATION; |
|
skb->sk = NULL; |
|
x25_send_frame(skb, neigh); |
} |
|
/* |
* This routine is called when a Diagnostic is required. |
*/ |
void x25_transmit_diagnostic(struct x25_neigh *neigh, unsigned char diag) |
{ |
struct sk_buff *skb; |
unsigned char *dptr; |
int len; |
|
len = X25_MAX_L2_LEN + X25_STD_MIN_LEN + 1; |
|
if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL) |
return; |
|
skb_reserve(skb, X25_MAX_L2_LEN); |
|
dptr = skb_put(skb, X25_STD_MIN_LEN + 1); |
|
*dptr++ = (neigh->extended) ? X25_GFI_EXTSEQ : X25_GFI_STDSEQ; |
*dptr++ = 0x00; |
*dptr++ = X25_DIAGNOSTIC; |
*dptr++ = diag; |
|
skb->sk = NULL; |
|
x25_send_frame(skb, neigh); |
} |
|
/* |
* This routine is called when a Clear Request is needed outside of the context |
* of a connected socket. |
*/ |
void x25_transmit_clear_request(struct x25_neigh *neigh, unsigned int lci, unsigned char cause) |
{ |
struct sk_buff *skb; |
unsigned char *dptr; |
int len; |
|
len = X25_MAX_L2_LEN + X25_STD_MIN_LEN + 2; |
|
if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL) |
return; |
|
skb_reserve(skb, X25_MAX_L2_LEN); |
|
dptr = skb_put(skb, X25_STD_MIN_LEN + 2); |
|
*dptr++ = ((lci >> 8) & 0x0F) | (neigh->extended ? X25_GFI_EXTSEQ : X25_GFI_STDSEQ); |
*dptr++ = ((lci >> 0) & 0xFF); |
*dptr++ = X25_CLEAR_REQUEST; |
*dptr++ = cause; |
*dptr++ = 0x00; |
|
skb->sk = NULL; |
|
x25_send_frame(skb, neigh); |
} |
|
void x25_transmit_link(struct sk_buff *skb, struct x25_neigh *neigh) |
{ |
switch (neigh->state) { |
case X25_LINK_STATE_0: |
skb_queue_tail(&neigh->queue, skb); |
neigh->state = X25_LINK_STATE_1; |
x25_establish_link(neigh); |
break; |
case X25_LINK_STATE_1: |
case X25_LINK_STATE_2: |
skb_queue_tail(&neigh->queue, skb); |
break; |
case X25_LINK_STATE_3: |
x25_send_frame(skb, neigh); |
break; |
} |
} |
|
/* |
* Called when the link layer has become established. |
*/ |
void x25_link_established(struct x25_neigh *neigh) |
{ |
switch (neigh->state) { |
case X25_LINK_STATE_0: |
neigh->state = X25_LINK_STATE_2; |
break; |
case X25_LINK_STATE_1: |
x25_transmit_restart_request(neigh); |
neigh->state = X25_LINK_STATE_2; |
x25_start_t20timer(neigh); |
break; |
} |
} |
|
/* |
* Called when the link layer has terminated, or an establishment |
* request has failed. |
*/ |
|
void x25_link_terminated(struct x25_neigh *neigh) |
{ |
neigh->state = X25_LINK_STATE_0; |
/* Out of order: clear existing virtual calls (X.25 03/93 4.6.3) */ |
x25_kill_by_neigh(neigh); |
} |
|
/* |
* Add a new device. |
*/ |
void x25_link_device_up(struct net_device *dev) |
{ |
struct x25_neigh *x25_neigh; |
unsigned long flags; |
|
if ((x25_neigh = kmalloc(sizeof(*x25_neigh), GFP_ATOMIC)) == NULL) |
return; |
|
skb_queue_head_init(&x25_neigh->queue); |
|
init_timer(&x25_neigh->t20timer); |
|
dev_hold(dev); |
x25_neigh->dev = dev; |
x25_neigh->state = X25_LINK_STATE_0; |
x25_neigh->extended = 0; |
x25_neigh->global_facil_mask = (X25_MASK_REVERSE | X25_MASK_THROUGHPUT | X25_MASK_PACKET_SIZE | X25_MASK_WINDOW_SIZE); /* enables negotiation */ |
x25_neigh->t20 = sysctl_x25_restart_request_timeout; |
|
save_flags(flags); cli(); |
x25_neigh->next = x25_neigh_list; |
x25_neigh_list = x25_neigh; |
restore_flags(flags); |
} |
|
static void x25_remove_neigh(struct x25_neigh *x25_neigh) |
{ |
struct x25_neigh *s; |
unsigned long flags; |
|
skb_queue_purge(&x25_neigh->queue); |
|
x25_stop_t20timer(x25_neigh); |
|
save_flags(flags); cli(); |
|
if ((s = x25_neigh_list) == x25_neigh) { |
x25_neigh_list = x25_neigh->next; |
restore_flags(flags); |
kfree(x25_neigh); |
return; |
} |
|
while (s != NULL && s->next != NULL) { |
if (s->next == x25_neigh) { |
s->next = x25_neigh->next; |
restore_flags(flags); |
kfree(x25_neigh); |
return; |
} |
|
s = s->next; |
} |
|
restore_flags(flags); |
} |
|
/* |
* A device has been removed, remove its links. |
*/ |
void x25_link_device_down(struct net_device *dev) |
{ |
struct x25_neigh *neigh, *x25_neigh = x25_neigh_list; |
|
while (x25_neigh != NULL) { |
neigh = x25_neigh; |
x25_neigh = x25_neigh->next; |
|
if (neigh->dev == dev){ |
x25_remove_neigh(neigh); |
dev_put(dev); |
} |
} |
} |
|
/* |
* Given a device, return the neighbour address. |
*/ |
struct x25_neigh *x25_get_neigh(struct net_device *dev) |
{ |
struct x25_neigh *x25_neigh; |
|
for (x25_neigh = x25_neigh_list; x25_neigh != NULL; x25_neigh = x25_neigh->next) |
if (x25_neigh->dev == dev) |
return x25_neigh; |
|
return NULL; |
} |
|
/* |
* Handle the ioctls that control the subscription functions. |
*/ |
int x25_subscr_ioctl(unsigned int cmd, void *arg) |
{ |
struct x25_subscrip_struct x25_subscr; |
struct x25_neigh *x25_neigh; |
struct net_device *dev; |
|
switch (cmd) { |
|
case SIOCX25GSUBSCRIP: |
if (copy_from_user(&x25_subscr, arg, sizeof(struct x25_subscrip_struct))) |
return -EFAULT; |
if ((dev = x25_dev_get(x25_subscr.device)) == NULL) |
return -EINVAL; |
if ((x25_neigh = x25_get_neigh(dev)) == NULL) { |
dev_put(dev); |
return -EINVAL; |
} |
dev_put(dev); |
x25_subscr.extended = x25_neigh->extended; |
x25_subscr.global_facil_mask = x25_neigh->global_facil_mask; |
if (copy_to_user(arg, &x25_subscr, sizeof(struct x25_subscrip_struct))) |
return -EFAULT; |
break; |
|
case SIOCX25SSUBSCRIP: |
if (copy_from_user(&x25_subscr, arg, sizeof(struct x25_subscrip_struct))) |
return -EFAULT; |
if ((dev = x25_dev_get(x25_subscr.device)) == NULL) |
return -EINVAL; |
if ((x25_neigh = x25_get_neigh(dev)) == NULL) { |
dev_put(dev); |
return -EINVAL; |
} |
dev_put(dev); |
if (x25_subscr.extended != 0 && x25_subscr.extended != 1) |
return -EINVAL; |
x25_neigh->extended = x25_subscr.extended; |
x25_neigh->global_facil_mask = x25_subscr.global_facil_mask; |
break; |
|
default: |
return -EINVAL; |
} |
|
return 0; |
} |
|
|
/* |
* Release all memory associated with X.25 neighbour structures. |
*/ |
void __exit x25_link_free(void) |
{ |
struct x25_neigh *neigh, *x25_neigh = x25_neigh_list; |
|
while (x25_neigh != NULL) { |
neigh = x25_neigh; |
x25_neigh = x25_neigh->next; |
|
x25_remove_neigh(neigh); |
} |
} |
/Makefile
0,0 → 1,19
# |
# Makefile for the Linux X.25 Packet layer. |
# |
# Note! Dependencies are done automagically by 'make dep', which also |
# removes any old dependencies. DON'T put your own dependencies here |
# unless it's something special (ie not a .c file). |
# |
# Note 2! The CFLAGS definition is now in the main makefile... |
|
O_TARGET := x25.o |
|
obj-y := af_x25.o x25_dev.o x25_facilities.o x25_in.o x25_link.o x25_out.o \ |
x25_route.o x25_subr.o x25_timer.o |
obj-m := $(O_TARGET) |
|
obj-$(CONFIG_SYSCTL) += sysctl_net_x25.o |
|
include $(TOPDIR)/Rules.make |
|
/x25_dev.c
0,0 → 1,250
/* |
* X.25 Packet Layer release 002 |
* |
* 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. |
* |
* This code REQUIRES 2.1.15 or higher |
* |
* This module: |
* This module is free software; you can redistribute it and/or |
* modify it under the terms of the GNU General Public License |
* as published by the Free Software Foundation; either version |
* 2 of the License, or (at your option) any later version. |
* |
* History |
* X.25 001 Jonathan Naylor Started coding. |
* 2000-09-04 Henner Eisen Prevent freeing a dangling skb. |
*/ |
|
#include <linux/config.h> |
#include <linux/errno.h> |
#include <linux/types.h> |
#include <linux/socket.h> |
#include <linux/in.h> |
#include <linux/kernel.h> |
#include <linux/sched.h> |
#include <linux/timer.h> |
#include <linux/string.h> |
#include <linux/sockios.h> |
#include <linux/net.h> |
#include <linux/stat.h> |
#include <linux/inet.h> |
#include <linux/netdevice.h> |
#include <linux/skbuff.h> |
#include <net/sock.h> |
#include <asm/segment.h> |
#include <asm/system.h> |
#include <asm/uaccess.h> |
#include <linux/fcntl.h> |
#include <linux/termios.h> /* For TIOCINQ/OUTQ */ |
#include <linux/mm.h> |
#include <linux/interrupt.h> |
#include <linux/notifier.h> |
#include <linux/proc_fs.h> |
#include <linux/if_arp.h> |
#include <net/x25.h> |
|
static int x25_receive_data(struct sk_buff *skb, struct x25_neigh *neigh) |
{ |
struct sock *sk; |
unsigned short frametype; |
unsigned int lci; |
|
frametype = skb->data[2]; |
lci = ((skb->data[0] << 8) & 0xF00) + ((skb->data[1] << 0) & 0x0FF); |
|
/* |
* LCI of zero is always for us, and its always a link control |
* frame. |
*/ |
if (lci == 0) { |
x25_link_control(skb, neigh, frametype); |
return 0; |
} |
|
/* |
* Find an existing socket. |
*/ |
if ((sk = x25_find_socket(lci, neigh)) != NULL) { |
int queued = 1; |
|
skb->h.raw = skb->data; |
bh_lock_sock(sk); |
if (!sk->lock.users) { |
queued = x25_process_rx_frame(sk, skb); |
} else { |
sk_add_backlog(sk, skb); |
} |
bh_unlock_sock(sk); |
return queued; |
} |
|
/* |
* Is is a Call Request ? if so process it. |
*/ |
if (frametype == X25_CALL_REQUEST) |
return x25_rx_call_request(skb, neigh, lci); |
|
/* |
* Its not a Call Request, nor is it a control frame. |
* Let caller throw it away. |
*/ |
/* |
x25_transmit_clear_request(neigh, lci, 0x0D); |
*/ |
printk(KERN_DEBUG "x25_receive_data(): unknown frame type %2x\n",frametype); |
|
return 0; |
} |
|
int x25_lapb_receive_frame(struct sk_buff *skb, struct net_device *dev, struct packet_type *ptype) |
{ |
struct x25_neigh *neigh; |
int queued; |
|
skb->sk = NULL; |
|
/* |
* Packet received from unrecognised device, throw it away. |
*/ |
if ((neigh = x25_get_neigh(dev)) == NULL) { |
printk(KERN_DEBUG "X.25: unknown neighbour - %s\n", dev->name); |
kfree_skb(skb); |
return 0; |
} |
|
switch (skb->data[0]) { |
case 0x00: |
skb_pull(skb, 1); |
queued = x25_receive_data(skb, neigh); |
if( ! queued ) |
/* We need to free the skb ourselves because |
* net_bh() won't care about our return code. |
*/ |
kfree_skb(skb); |
return 0; |
|
case 0x01: |
x25_link_established(neigh); |
kfree_skb(skb); |
return 0; |
|
case 0x02: |
x25_link_terminated(neigh); |
kfree_skb(skb); |
return 0; |
|
case 0x03: |
kfree_skb(skb); |
return 0; |
|
default: |
kfree_skb(skb); |
return 0; |
} |
} |
|
int x25_llc_receive_frame(struct sk_buff *skb, struct net_device *dev, struct packet_type *ptype) |
{ |
struct x25_neigh *neigh; |
|
skb->sk = NULL; |
|
/* |
* Packet received from unrecognised device, throw it away. |
*/ |
if ((neigh = x25_get_neigh(dev)) == NULL) { |
printk(KERN_DEBUG "X.25: unknown_neighbour - %s\n", dev->name); |
kfree_skb(skb); |
return 0; |
} |
|
return x25_receive_data(skb, neigh); |
} |
|
void x25_establish_link(struct x25_neigh *neigh) |
{ |
struct sk_buff *skb; |
unsigned char *ptr; |
|
switch (neigh->dev->type) { |
case ARPHRD_X25: |
if ((skb = alloc_skb(1, GFP_ATOMIC)) == NULL) { |
printk(KERN_ERR "x25_dev: out of memory\n"); |
return; |
} |
ptr = skb_put(skb, 1); |
*ptr = 0x01; |
break; |
|
#if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE) |
case ARPHRD_ETHER: |
return; |
#endif |
default: |
return; |
} |
|
skb->protocol = htons(ETH_P_X25); |
skb->dev = neigh->dev; |
|
dev_queue_xmit(skb); |
} |
|
void x25_terminate_link(struct x25_neigh *neigh) |
{ |
struct sk_buff *skb; |
unsigned char *ptr; |
|
switch (neigh->dev->type) { |
case ARPHRD_X25: |
if ((skb = alloc_skb(1, GFP_ATOMIC)) == NULL) { |
printk(KERN_ERR "x25_dev: out of memory\n"); |
return; |
} |
ptr = skb_put(skb, 1); |
*ptr = 0x02; |
break; |
|
#if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE) |
case ARPHRD_ETHER: |
return; |
#endif |
default: |
return; |
} |
|
skb->protocol = htons(ETH_P_X25); |
skb->dev = neigh->dev; |
|
dev_queue_xmit(skb); |
} |
|
void x25_send_frame(struct sk_buff *skb, struct x25_neigh *neigh) |
{ |
unsigned char *dptr; |
|
skb->nh.raw = skb->data; |
|
switch (neigh->dev->type) { |
case ARPHRD_X25: |
dptr = skb_push(skb, 1); |
*dptr = 0x00; |
break; |
|
#if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE) |
case ARPHRD_ETHER: |
kfree_skb(skb); |
return; |
#endif |
default: |
kfree_skb(skb); |
return; |
} |
|
skb->protocol = htons(ETH_P_X25); |
skb->dev = neigh->dev; |
|
dev_queue_xmit(skb); |
} |
/x25_route.c
0,0 → 1,271
/* |
* X.25 Packet Layer release 002 |
* |
* 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. |
* |
* This code REQUIRES 2.1.15 or higher |
* |
* This module: |
* This module is free software; you can redistribute it and/or |
* modify it under the terms of the GNU General Public License |
* as published by the Free Software Foundation; either version |
* 2 of the License, or (at your option) any later version. |
* |
* History |
* X.25 001 Jonathan Naylor Started coding. |
*/ |
|
#include <linux/config.h> |
#include <linux/errno.h> |
#include <linux/types.h> |
#include <linux/socket.h> |
#include <linux/in.h> |
#include <linux/kernel.h> |
#include <linux/sched.h> |
#include <linux/timer.h> |
#include <linux/string.h> |
#include <linux/sockios.h> |
#include <linux/net.h> |
#include <linux/inet.h> |
#include <linux/netdevice.h> |
#include <net/arp.h> |
#include <linux/if_arp.h> |
#include <linux/skbuff.h> |
#include <net/sock.h> |
#include <asm/segment.h> |
#include <asm/system.h> |
#include <asm/uaccess.h> |
#include <linux/fcntl.h> |
#include <linux/termios.h> /* For TIOCINQ/OUTQ */ |
#include <linux/mm.h> |
#include <linux/interrupt.h> |
#include <linux/notifier.h> |
#include <linux/init.h> |
#include <net/x25.h> |
|
static struct x25_route *x25_route_list /* = NULL initially */; |
|
/* |
* Add a new route. |
*/ |
static int x25_add_route(x25_address *address, unsigned int sigdigits, struct net_device *dev) |
{ |
struct x25_route *x25_route; |
unsigned long flags; |
|
for (x25_route = x25_route_list; x25_route != NULL; x25_route = x25_route->next) |
if (memcmp(&x25_route->address, address, sigdigits) == 0 && x25_route->sigdigits == sigdigits) |
return -EINVAL; |
|
if ((x25_route = kmalloc(sizeof(*x25_route), GFP_ATOMIC)) == NULL) |
return -ENOMEM; |
|
strcpy(x25_route->address.x25_addr, "000000000000000"); |
memcpy(x25_route->address.x25_addr, address->x25_addr, sigdigits); |
|
x25_route->sigdigits = sigdigits; |
x25_route->dev = dev; |
|
save_flags(flags); cli(); |
x25_route->next = x25_route_list; |
x25_route_list = x25_route; |
restore_flags(flags); |
|
return 0; |
} |
|
static void x25_remove_route(struct x25_route *x25_route) |
{ |
struct x25_route *s; |
unsigned long flags; |
|
save_flags(flags); |
cli(); |
|
if ((s = x25_route_list) == x25_route) { |
x25_route_list = x25_route->next; |
restore_flags(flags); |
kfree(x25_route); |
return; |
} |
|
while (s != NULL && s->next != NULL) { |
if (s->next == x25_route) { |
s->next = x25_route->next; |
restore_flags(flags); |
kfree(x25_route); |
return; |
} |
|
s = s->next; |
} |
|
restore_flags(flags); |
} |
|
static int x25_del_route(x25_address *address, unsigned int sigdigits, struct net_device *dev) |
{ |
struct x25_route *x25_route; |
|
for (x25_route = x25_route_list; x25_route != NULL; x25_route = x25_route->next) { |
if (memcmp(&x25_route->address, address, sigdigits) == 0 && x25_route->sigdigits == sigdigits && x25_route->dev == dev) { |
x25_remove_route(x25_route); |
return 0; |
} |
} |
|
return -EINVAL; |
} |
|
/* |
* A device has been removed, remove its routes. |
*/ |
void x25_route_device_down(struct net_device *dev) |
{ |
struct x25_route *route, *x25_route = x25_route_list; |
|
while (x25_route != NULL) { |
route = x25_route; |
x25_route = x25_route->next; |
|
if (route->dev == dev) |
x25_remove_route(route); |
} |
} |
|
/* |
* Check that the device given is a valid X.25 interface that is "up". |
*/ |
struct net_device *x25_dev_get(char *devname) |
{ |
struct net_device *dev; |
|
if ((dev = dev_get_by_name(devname)) == NULL) |
return NULL; |
|
if ((dev->flags & IFF_UP) && (dev->type == ARPHRD_X25 |
#if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE) |
|| dev->type == ARPHRD_ETHER |
#endif |
)) |
return dev; |
|
dev_put(dev); |
|
return NULL; |
} |
|
/* |
* Find a device given an X.25 address. |
*/ |
struct net_device *x25_get_route(x25_address *addr) |
{ |
struct x25_route *route, *use = NULL; |
|
for (route = x25_route_list; route != NULL; route = route->next) { |
if (memcmp(&route->address, addr, route->sigdigits) == 0) { |
if (use == NULL) { |
use = route; |
} else { |
if (route->sigdigits > use->sigdigits) |
use = route; |
} |
} |
} |
|
return (use != NULL) ? use->dev : NULL; |
} |
|
/* |
* Handle the ioctls that control the routing functions. |
*/ |
int x25_route_ioctl(unsigned int cmd, void *arg) |
{ |
struct x25_route_struct x25_route; |
struct net_device *dev; |
int err; |
|
switch (cmd) { |
|
case SIOCADDRT: |
if (copy_from_user(&x25_route, arg, sizeof(struct x25_route_struct))) |
return -EFAULT; |
if (x25_route.sigdigits < 0 || x25_route.sigdigits > 15) |
return -EINVAL; |
if ((dev = x25_dev_get(x25_route.device)) == NULL) |
return -EINVAL; |
err = x25_add_route(&x25_route.address, x25_route.sigdigits, dev); |
dev_put(dev); |
return err; |
|
case SIOCDELRT: |
if (copy_from_user(&x25_route, arg, sizeof(struct x25_route_struct))) |
return -EFAULT; |
if (x25_route.sigdigits < 0 || x25_route.sigdigits > 15) |
return -EINVAL; |
if ((dev = x25_dev_get(x25_route.device)) == NULL) |
return -EINVAL; |
err = x25_del_route(&x25_route.address, x25_route.sigdigits, dev); |
dev_put(dev); |
return err; |
|
default: |
return -EINVAL; |
} |
|
return 0; |
} |
|
int x25_routes_get_info(char *buffer, char **start, off_t offset, int length) |
{ |
struct x25_route *x25_route; |
int len = 0; |
off_t pos = 0; |
off_t begin = 0; |
|
cli(); |
|
len += sprintf(buffer, "address digits device\n"); |
|
for (x25_route = x25_route_list; x25_route != NULL; x25_route = x25_route->next) { |
len += sprintf(buffer + len, "%-15s %-6d %-5s\n", |
x25_route->address.x25_addr, |
x25_route->sigdigits, |
(x25_route->dev != NULL) ? x25_route->dev->name : "???"); |
|
pos = begin + len; |
|
if (pos < offset) { |
len = 0; |
begin = pos; |
} |
|
if (pos > offset + length) |
break; |
} |
|
sti(); |
|
*start = buffer + (offset - begin); |
len -= (offset - begin); |
|
if (len > length) len = length; |
|
return len; |
} |
|
/* |
* Release all memory associated with X.25 routing structures. |
*/ |
void __exit x25_route_free(void) |
{ |
struct x25_route *route, *x25_route = x25_route_list; |
|
while (x25_route != NULL) { |
route = x25_route; |
x25_route = x25_route->next; |
|
x25_remove_route(route); |
} |
} |