OpenCores
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);
}
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.