162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * X.25 Packet Layer release 002 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * This is ALPHA test software. This code may break your machine, 662306a36Sopenharmony_ci * randomly fail to work with new releases, misbehave and/or generally 762306a36Sopenharmony_ci * screw up. It might even work. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * This code REQUIRES 2.1.15 or higher 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * History 1262306a36Sopenharmony_ci * X.25 001 Jonathan Naylor Started coding. 1362306a36Sopenharmony_ci * X.25 002 Jonathan Naylor Centralised disconnect handling. 1462306a36Sopenharmony_ci * New timer architecture. 1562306a36Sopenharmony_ci * 2000-03-11 Henner Eisen MSG_EOR handling more POSIX compliant. 1662306a36Sopenharmony_ci * 2000-03-22 Daniela Squassoni Allowed disabling/enabling of 1762306a36Sopenharmony_ci * facilities negotiation and increased 1862306a36Sopenharmony_ci * the throughput upper limit. 1962306a36Sopenharmony_ci * 2000-08-27 Arnaldo C. Melo s/suser/capable/ + micro cleanups 2062306a36Sopenharmony_ci * 2000-09-04 Henner Eisen Set sock->state in x25_accept(). 2162306a36Sopenharmony_ci * Fixed x25_output() related skb leakage. 2262306a36Sopenharmony_ci * 2000-10-02 Henner Eisen Made x25_kick() single threaded per socket. 2362306a36Sopenharmony_ci * 2000-10-27 Henner Eisen MSG_DONTWAIT for fragment allocation. 2462306a36Sopenharmony_ci * 2000-11-14 Henner Eisen Closing datalink from NETDEV_GOING_DOWN 2562306a36Sopenharmony_ci * 2002-10-06 Arnaldo C. Melo Get rid of cli/sti, move proc stuff to 2662306a36Sopenharmony_ci * x25_proc.c, using seq_file 2762306a36Sopenharmony_ci * 2005-04-02 Shaun Pereira Selective sub address matching 2862306a36Sopenharmony_ci * with call user data 2962306a36Sopenharmony_ci * 2005-04-15 Shaun Pereira Fast select with no restriction on 3062306a36Sopenharmony_ci * response 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define pr_fmt(fmt) "X25: " fmt 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#include <linux/module.h> 3662306a36Sopenharmony_ci#include <linux/capability.h> 3762306a36Sopenharmony_ci#include <linux/errno.h> 3862306a36Sopenharmony_ci#include <linux/kernel.h> 3962306a36Sopenharmony_ci#include <linux/sched/signal.h> 4062306a36Sopenharmony_ci#include <linux/timer.h> 4162306a36Sopenharmony_ci#include <linux/string.h> 4262306a36Sopenharmony_ci#include <linux/net.h> 4362306a36Sopenharmony_ci#include <linux/netdevice.h> 4462306a36Sopenharmony_ci#include <linux/if_arp.h> 4562306a36Sopenharmony_ci#include <linux/skbuff.h> 4662306a36Sopenharmony_ci#include <linux/slab.h> 4762306a36Sopenharmony_ci#include <net/sock.h> 4862306a36Sopenharmony_ci#include <net/tcp_states.h> 4962306a36Sopenharmony_ci#include <linux/uaccess.h> 5062306a36Sopenharmony_ci#include <linux/fcntl.h> 5162306a36Sopenharmony_ci#include <linux/termios.h> /* For TIOCINQ/OUTQ */ 5262306a36Sopenharmony_ci#include <linux/notifier.h> 5362306a36Sopenharmony_ci#include <linux/init.h> 5462306a36Sopenharmony_ci#include <linux/compat.h> 5562306a36Sopenharmony_ci#include <linux/ctype.h> 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#include <net/x25.h> 5862306a36Sopenharmony_ci#include <net/compat.h> 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ciint sysctl_x25_restart_request_timeout = X25_DEFAULT_T20; 6162306a36Sopenharmony_ciint sysctl_x25_call_request_timeout = X25_DEFAULT_T21; 6262306a36Sopenharmony_ciint sysctl_x25_reset_request_timeout = X25_DEFAULT_T22; 6362306a36Sopenharmony_ciint sysctl_x25_clear_request_timeout = X25_DEFAULT_T23; 6462306a36Sopenharmony_ciint sysctl_x25_ack_holdback_timeout = X25_DEFAULT_T2; 6562306a36Sopenharmony_ciint sysctl_x25_forward = 0; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ciHLIST_HEAD(x25_list); 6862306a36Sopenharmony_ciDEFINE_RWLOCK(x25_list_lock); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic const struct proto_ops x25_proto_ops; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic const struct x25_address null_x25_address = {" "}; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 7562306a36Sopenharmony_cistruct compat_x25_subscrip_struct { 7662306a36Sopenharmony_ci char device[200-sizeof(compat_ulong_t)]; 7762306a36Sopenharmony_ci compat_ulong_t global_facil_mask; 7862306a36Sopenharmony_ci compat_uint_t extended; 7962306a36Sopenharmony_ci}; 8062306a36Sopenharmony_ci#endif 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ciint x25_parse_address_block(struct sk_buff *skb, 8462306a36Sopenharmony_ci struct x25_address *called_addr, 8562306a36Sopenharmony_ci struct x25_address *calling_addr) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci unsigned char len; 8862306a36Sopenharmony_ci int needed; 8962306a36Sopenharmony_ci int rc; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (!pskb_may_pull(skb, 1)) { 9262306a36Sopenharmony_ci /* packet has no address block */ 9362306a36Sopenharmony_ci rc = 0; 9462306a36Sopenharmony_ci goto empty; 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci len = *skb->data; 9862306a36Sopenharmony_ci needed = 1 + ((len >> 4) + (len & 0x0f) + 1) / 2; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci if (!pskb_may_pull(skb, needed)) { 10162306a36Sopenharmony_ci /* packet is too short to hold the addresses it claims 10262306a36Sopenharmony_ci to hold */ 10362306a36Sopenharmony_ci rc = -1; 10462306a36Sopenharmony_ci goto empty; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci return x25_addr_ntoa(skb->data, called_addr, calling_addr); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ciempty: 11062306a36Sopenharmony_ci *called_addr->x25_addr = 0; 11162306a36Sopenharmony_ci *calling_addr->x25_addr = 0; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci return rc; 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ciint x25_addr_ntoa(unsigned char *p, struct x25_address *called_addr, 11862306a36Sopenharmony_ci struct x25_address *calling_addr) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci unsigned int called_len, calling_len; 12162306a36Sopenharmony_ci char *called, *calling; 12262306a36Sopenharmony_ci unsigned int i; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci called_len = (*p >> 0) & 0x0F; 12562306a36Sopenharmony_ci calling_len = (*p >> 4) & 0x0F; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci called = called_addr->x25_addr; 12862306a36Sopenharmony_ci calling = calling_addr->x25_addr; 12962306a36Sopenharmony_ci p++; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci for (i = 0; i < (called_len + calling_len); i++) { 13262306a36Sopenharmony_ci if (i < called_len) { 13362306a36Sopenharmony_ci if (i % 2 != 0) { 13462306a36Sopenharmony_ci *called++ = ((*p >> 0) & 0x0F) + '0'; 13562306a36Sopenharmony_ci p++; 13662306a36Sopenharmony_ci } else { 13762306a36Sopenharmony_ci *called++ = ((*p >> 4) & 0x0F) + '0'; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci } else { 14062306a36Sopenharmony_ci if (i % 2 != 0) { 14162306a36Sopenharmony_ci *calling++ = ((*p >> 0) & 0x0F) + '0'; 14262306a36Sopenharmony_ci p++; 14362306a36Sopenharmony_ci } else { 14462306a36Sopenharmony_ci *calling++ = ((*p >> 4) & 0x0F) + '0'; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci *called = *calling = '\0'; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci return 1 + (called_len + calling_len + 1) / 2; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ciint x25_addr_aton(unsigned char *p, struct x25_address *called_addr, 15562306a36Sopenharmony_ci struct x25_address *calling_addr) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci unsigned int called_len, calling_len; 15862306a36Sopenharmony_ci char *called, *calling; 15962306a36Sopenharmony_ci int i; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci called = called_addr->x25_addr; 16262306a36Sopenharmony_ci calling = calling_addr->x25_addr; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci called_len = strlen(called); 16562306a36Sopenharmony_ci calling_len = strlen(calling); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci *p++ = (calling_len << 4) | (called_len << 0); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci for (i = 0; i < (called_len + calling_len); i++) { 17062306a36Sopenharmony_ci if (i < called_len) { 17162306a36Sopenharmony_ci if (i % 2 != 0) { 17262306a36Sopenharmony_ci *p |= (*called++ - '0') << 0; 17362306a36Sopenharmony_ci p++; 17462306a36Sopenharmony_ci } else { 17562306a36Sopenharmony_ci *p = 0x00; 17662306a36Sopenharmony_ci *p |= (*called++ - '0') << 4; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci } else { 17962306a36Sopenharmony_ci if (i % 2 != 0) { 18062306a36Sopenharmony_ci *p |= (*calling++ - '0') << 0; 18162306a36Sopenharmony_ci p++; 18262306a36Sopenharmony_ci } else { 18362306a36Sopenharmony_ci *p = 0x00; 18462306a36Sopenharmony_ci *p |= (*calling++ - '0') << 4; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci return 1 + (called_len + calling_len + 1) / 2; 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci/* 19362306a36Sopenharmony_ci * Socket removal during an interrupt is now safe. 19462306a36Sopenharmony_ci */ 19562306a36Sopenharmony_cistatic void x25_remove_socket(struct sock *sk) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci write_lock_bh(&x25_list_lock); 19862306a36Sopenharmony_ci sk_del_node_init(sk); 19962306a36Sopenharmony_ci write_unlock_bh(&x25_list_lock); 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci/* 20362306a36Sopenharmony_ci * Handle device status changes. 20462306a36Sopenharmony_ci */ 20562306a36Sopenharmony_cistatic int x25_device_event(struct notifier_block *this, unsigned long event, 20662306a36Sopenharmony_ci void *ptr) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci struct net_device *dev = netdev_notifier_info_to_dev(ptr); 20962306a36Sopenharmony_ci struct x25_neigh *nb; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (!net_eq(dev_net(dev), &init_net)) 21262306a36Sopenharmony_ci return NOTIFY_DONE; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (dev->type == ARPHRD_X25) { 21562306a36Sopenharmony_ci switch (event) { 21662306a36Sopenharmony_ci case NETDEV_REGISTER: 21762306a36Sopenharmony_ci case NETDEV_POST_TYPE_CHANGE: 21862306a36Sopenharmony_ci x25_link_device_up(dev); 21962306a36Sopenharmony_ci break; 22062306a36Sopenharmony_ci case NETDEV_DOWN: 22162306a36Sopenharmony_ci nb = x25_get_neigh(dev); 22262306a36Sopenharmony_ci if (nb) { 22362306a36Sopenharmony_ci x25_link_terminated(nb); 22462306a36Sopenharmony_ci x25_neigh_put(nb); 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci x25_route_device_down(dev); 22762306a36Sopenharmony_ci break; 22862306a36Sopenharmony_ci case NETDEV_PRE_TYPE_CHANGE: 22962306a36Sopenharmony_ci case NETDEV_UNREGISTER: 23062306a36Sopenharmony_ci x25_link_device_down(dev); 23162306a36Sopenharmony_ci break; 23262306a36Sopenharmony_ci case NETDEV_CHANGE: 23362306a36Sopenharmony_ci if (!netif_carrier_ok(dev)) { 23462306a36Sopenharmony_ci nb = x25_get_neigh(dev); 23562306a36Sopenharmony_ci if (nb) { 23662306a36Sopenharmony_ci x25_link_terminated(nb); 23762306a36Sopenharmony_ci x25_neigh_put(nb); 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci break; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci return NOTIFY_DONE; 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci/* 24862306a36Sopenharmony_ci * Add a socket to the bound sockets list. 24962306a36Sopenharmony_ci */ 25062306a36Sopenharmony_cistatic void x25_insert_socket(struct sock *sk) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci write_lock_bh(&x25_list_lock); 25362306a36Sopenharmony_ci sk_add_node(sk, &x25_list); 25462306a36Sopenharmony_ci write_unlock_bh(&x25_list_lock); 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci/* 25862306a36Sopenharmony_ci * Find a socket that wants to accept the Call Request we just 25962306a36Sopenharmony_ci * received. Check the full list for an address/cud match. 26062306a36Sopenharmony_ci * If no cuds match return the next_best thing, an address match. 26162306a36Sopenharmony_ci * Note: if a listening socket has cud set it must only get calls 26262306a36Sopenharmony_ci * with matching cud. 26362306a36Sopenharmony_ci */ 26462306a36Sopenharmony_cistatic struct sock *x25_find_listener(struct x25_address *addr, 26562306a36Sopenharmony_ci struct sk_buff *skb) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci struct sock *s; 26862306a36Sopenharmony_ci struct sock *next_best; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci read_lock_bh(&x25_list_lock); 27162306a36Sopenharmony_ci next_best = NULL; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci sk_for_each(s, &x25_list) 27462306a36Sopenharmony_ci if ((!strcmp(addr->x25_addr, 27562306a36Sopenharmony_ci x25_sk(s)->source_addr.x25_addr) || 27662306a36Sopenharmony_ci !strcmp(x25_sk(s)->source_addr.x25_addr, 27762306a36Sopenharmony_ci null_x25_address.x25_addr)) && 27862306a36Sopenharmony_ci s->sk_state == TCP_LISTEN) { 27962306a36Sopenharmony_ci /* 28062306a36Sopenharmony_ci * Found a listening socket, now check the incoming 28162306a36Sopenharmony_ci * call user data vs this sockets call user data 28262306a36Sopenharmony_ci */ 28362306a36Sopenharmony_ci if (x25_sk(s)->cudmatchlength > 0 && 28462306a36Sopenharmony_ci skb->len >= x25_sk(s)->cudmatchlength) { 28562306a36Sopenharmony_ci if((memcmp(x25_sk(s)->calluserdata.cuddata, 28662306a36Sopenharmony_ci skb->data, 28762306a36Sopenharmony_ci x25_sk(s)->cudmatchlength)) == 0) { 28862306a36Sopenharmony_ci sock_hold(s); 28962306a36Sopenharmony_ci goto found; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci } else 29262306a36Sopenharmony_ci next_best = s; 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci if (next_best) { 29562306a36Sopenharmony_ci s = next_best; 29662306a36Sopenharmony_ci sock_hold(s); 29762306a36Sopenharmony_ci goto found; 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci s = NULL; 30062306a36Sopenharmony_cifound: 30162306a36Sopenharmony_ci read_unlock_bh(&x25_list_lock); 30262306a36Sopenharmony_ci return s; 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci/* 30662306a36Sopenharmony_ci * Find a connected X.25 socket given my LCI and neighbour. 30762306a36Sopenharmony_ci */ 30862306a36Sopenharmony_cistatic struct sock *__x25_find_socket(unsigned int lci, struct x25_neigh *nb) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci struct sock *s; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci sk_for_each(s, &x25_list) 31362306a36Sopenharmony_ci if (x25_sk(s)->lci == lci && x25_sk(s)->neighbour == nb) { 31462306a36Sopenharmony_ci sock_hold(s); 31562306a36Sopenharmony_ci goto found; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci s = NULL; 31862306a36Sopenharmony_cifound: 31962306a36Sopenharmony_ci return s; 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistruct sock *x25_find_socket(unsigned int lci, struct x25_neigh *nb) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci struct sock *s; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci read_lock_bh(&x25_list_lock); 32762306a36Sopenharmony_ci s = __x25_find_socket(lci, nb); 32862306a36Sopenharmony_ci read_unlock_bh(&x25_list_lock); 32962306a36Sopenharmony_ci return s; 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci/* 33362306a36Sopenharmony_ci * Find a unique LCI for a given device. 33462306a36Sopenharmony_ci */ 33562306a36Sopenharmony_cistatic unsigned int x25_new_lci(struct x25_neigh *nb) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci unsigned int lci = 1; 33862306a36Sopenharmony_ci struct sock *sk; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci while ((sk = x25_find_socket(lci, nb)) != NULL) { 34162306a36Sopenharmony_ci sock_put(sk); 34262306a36Sopenharmony_ci if (++lci == 4096) { 34362306a36Sopenharmony_ci lci = 0; 34462306a36Sopenharmony_ci break; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci cond_resched(); 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci return lci; 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci/* 35362306a36Sopenharmony_ci * Deferred destroy. 35462306a36Sopenharmony_ci */ 35562306a36Sopenharmony_cistatic void __x25_destroy_socket(struct sock *); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci/* 35862306a36Sopenharmony_ci * handler for deferred kills. 35962306a36Sopenharmony_ci */ 36062306a36Sopenharmony_cistatic void x25_destroy_timer(struct timer_list *t) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci struct sock *sk = from_timer(sk, t, sk_timer); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci x25_destroy_socket_from_timer(sk); 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci/* 36862306a36Sopenharmony_ci * This is called from user mode and the timers. Thus it protects itself 36962306a36Sopenharmony_ci * against interrupting users but doesn't worry about being called during 37062306a36Sopenharmony_ci * work. Once it is removed from the queue no interrupt or bottom half 37162306a36Sopenharmony_ci * will touch it and we are (fairly 8-) ) safe. 37262306a36Sopenharmony_ci * Not static as it's used by the timer 37362306a36Sopenharmony_ci */ 37462306a36Sopenharmony_cistatic void __x25_destroy_socket(struct sock *sk) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci struct sk_buff *skb; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci x25_stop_heartbeat(sk); 37962306a36Sopenharmony_ci x25_stop_timer(sk); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci x25_remove_socket(sk); 38262306a36Sopenharmony_ci x25_clear_queues(sk); /* Flush the queues */ 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) { 38562306a36Sopenharmony_ci if (skb->sk != sk) { /* A pending connection */ 38662306a36Sopenharmony_ci /* 38762306a36Sopenharmony_ci * Queue the unaccepted socket for death 38862306a36Sopenharmony_ci */ 38962306a36Sopenharmony_ci skb->sk->sk_state = TCP_LISTEN; 39062306a36Sopenharmony_ci sock_set_flag(skb->sk, SOCK_DEAD); 39162306a36Sopenharmony_ci x25_start_heartbeat(skb->sk); 39262306a36Sopenharmony_ci x25_sk(skb->sk)->state = X25_STATE_0; 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci kfree_skb(skb); 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci if (sk_has_allocations(sk)) { 39962306a36Sopenharmony_ci /* Defer: outstanding buffers */ 40062306a36Sopenharmony_ci sk->sk_timer.expires = jiffies + 10 * HZ; 40162306a36Sopenharmony_ci sk->sk_timer.function = x25_destroy_timer; 40262306a36Sopenharmony_ci add_timer(&sk->sk_timer); 40362306a36Sopenharmony_ci } else { 40462306a36Sopenharmony_ci /* drop last reference so sock_put will free */ 40562306a36Sopenharmony_ci __sock_put(sk); 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_civoid x25_destroy_socket_from_timer(struct sock *sk) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci sock_hold(sk); 41262306a36Sopenharmony_ci bh_lock_sock(sk); 41362306a36Sopenharmony_ci __x25_destroy_socket(sk); 41462306a36Sopenharmony_ci bh_unlock_sock(sk); 41562306a36Sopenharmony_ci sock_put(sk); 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci/* 41962306a36Sopenharmony_ci * Handling for system calls applied via the various interfaces to a 42062306a36Sopenharmony_ci * X.25 socket object. 42162306a36Sopenharmony_ci */ 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_cistatic int x25_setsockopt(struct socket *sock, int level, int optname, 42462306a36Sopenharmony_ci sockptr_t optval, unsigned int optlen) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci int opt; 42762306a36Sopenharmony_ci struct sock *sk = sock->sk; 42862306a36Sopenharmony_ci int rc = -ENOPROTOOPT; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci if (level != SOL_X25 || optname != X25_QBITINCL) 43162306a36Sopenharmony_ci goto out; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci rc = -EINVAL; 43462306a36Sopenharmony_ci if (optlen < sizeof(int)) 43562306a36Sopenharmony_ci goto out; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci rc = -EFAULT; 43862306a36Sopenharmony_ci if (copy_from_sockptr(&opt, optval, sizeof(int))) 43962306a36Sopenharmony_ci goto out; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci if (opt) 44262306a36Sopenharmony_ci set_bit(X25_Q_BIT_FLAG, &x25_sk(sk)->flags); 44362306a36Sopenharmony_ci else 44462306a36Sopenharmony_ci clear_bit(X25_Q_BIT_FLAG, &x25_sk(sk)->flags); 44562306a36Sopenharmony_ci rc = 0; 44662306a36Sopenharmony_ciout: 44762306a36Sopenharmony_ci return rc; 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_cistatic int x25_getsockopt(struct socket *sock, int level, int optname, 45162306a36Sopenharmony_ci char __user *optval, int __user *optlen) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci struct sock *sk = sock->sk; 45462306a36Sopenharmony_ci int val, len, rc = -ENOPROTOOPT; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci if (level != SOL_X25 || optname != X25_QBITINCL) 45762306a36Sopenharmony_ci goto out; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci rc = -EFAULT; 46062306a36Sopenharmony_ci if (get_user(len, optlen)) 46162306a36Sopenharmony_ci goto out; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci rc = -EINVAL; 46462306a36Sopenharmony_ci if (len < 0) 46562306a36Sopenharmony_ci goto out; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci len = min_t(unsigned int, len, sizeof(int)); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci rc = -EFAULT; 47062306a36Sopenharmony_ci if (put_user(len, optlen)) 47162306a36Sopenharmony_ci goto out; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci val = test_bit(X25_Q_BIT_FLAG, &x25_sk(sk)->flags); 47462306a36Sopenharmony_ci rc = copy_to_user(optval, &val, len) ? -EFAULT : 0; 47562306a36Sopenharmony_ciout: 47662306a36Sopenharmony_ci return rc; 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistatic int x25_listen(struct socket *sock, int backlog) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci struct sock *sk = sock->sk; 48262306a36Sopenharmony_ci int rc = -EOPNOTSUPP; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci lock_sock(sk); 48562306a36Sopenharmony_ci if (sock->state != SS_UNCONNECTED) { 48662306a36Sopenharmony_ci rc = -EINVAL; 48762306a36Sopenharmony_ci release_sock(sk); 48862306a36Sopenharmony_ci return rc; 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci if (sk->sk_state != TCP_LISTEN) { 49262306a36Sopenharmony_ci memset(&x25_sk(sk)->dest_addr, 0, X25_ADDR_LEN); 49362306a36Sopenharmony_ci sk->sk_max_ack_backlog = backlog; 49462306a36Sopenharmony_ci sk->sk_state = TCP_LISTEN; 49562306a36Sopenharmony_ci rc = 0; 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci release_sock(sk); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci return rc; 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_cistatic struct proto x25_proto = { 50362306a36Sopenharmony_ci .name = "X25", 50462306a36Sopenharmony_ci .owner = THIS_MODULE, 50562306a36Sopenharmony_ci .obj_size = sizeof(struct x25_sock), 50662306a36Sopenharmony_ci}; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_cistatic struct sock *x25_alloc_socket(struct net *net, int kern) 50962306a36Sopenharmony_ci{ 51062306a36Sopenharmony_ci struct x25_sock *x25; 51162306a36Sopenharmony_ci struct sock *sk = sk_alloc(net, AF_X25, GFP_ATOMIC, &x25_proto, kern); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci if (!sk) 51462306a36Sopenharmony_ci goto out; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci sock_init_data(NULL, sk); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci x25 = x25_sk(sk); 51962306a36Sopenharmony_ci skb_queue_head_init(&x25->ack_queue); 52062306a36Sopenharmony_ci skb_queue_head_init(&x25->fragment_queue); 52162306a36Sopenharmony_ci skb_queue_head_init(&x25->interrupt_in_queue); 52262306a36Sopenharmony_ci skb_queue_head_init(&x25->interrupt_out_queue); 52362306a36Sopenharmony_ciout: 52462306a36Sopenharmony_ci return sk; 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_cistatic int x25_create(struct net *net, struct socket *sock, int protocol, 52862306a36Sopenharmony_ci int kern) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci struct sock *sk; 53162306a36Sopenharmony_ci struct x25_sock *x25; 53262306a36Sopenharmony_ci int rc = -EAFNOSUPPORT; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci if (!net_eq(net, &init_net)) 53562306a36Sopenharmony_ci goto out; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci rc = -ESOCKTNOSUPPORT; 53862306a36Sopenharmony_ci if (sock->type != SOCK_SEQPACKET) 53962306a36Sopenharmony_ci goto out; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci rc = -EINVAL; 54262306a36Sopenharmony_ci if (protocol) 54362306a36Sopenharmony_ci goto out; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci rc = -ENOMEM; 54662306a36Sopenharmony_ci if ((sk = x25_alloc_socket(net, kern)) == NULL) 54762306a36Sopenharmony_ci goto out; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci x25 = x25_sk(sk); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci sock_init_data(sock, sk); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci x25_init_timers(sk); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci sock->ops = &x25_proto_ops; 55662306a36Sopenharmony_ci sk->sk_protocol = protocol; 55762306a36Sopenharmony_ci sk->sk_backlog_rcv = x25_backlog_rcv; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci x25->t21 = sysctl_x25_call_request_timeout; 56062306a36Sopenharmony_ci x25->t22 = sysctl_x25_reset_request_timeout; 56162306a36Sopenharmony_ci x25->t23 = sysctl_x25_clear_request_timeout; 56262306a36Sopenharmony_ci x25->t2 = sysctl_x25_ack_holdback_timeout; 56362306a36Sopenharmony_ci x25->state = X25_STATE_0; 56462306a36Sopenharmony_ci x25->cudmatchlength = 0; 56562306a36Sopenharmony_ci set_bit(X25_ACCPT_APPRV_FLAG, &x25->flags); /* normally no cud */ 56662306a36Sopenharmony_ci /* on call accept */ 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci x25->facilities.winsize_in = X25_DEFAULT_WINDOW_SIZE; 56962306a36Sopenharmony_ci x25->facilities.winsize_out = X25_DEFAULT_WINDOW_SIZE; 57062306a36Sopenharmony_ci x25->facilities.pacsize_in = X25_DEFAULT_PACKET_SIZE; 57162306a36Sopenharmony_ci x25->facilities.pacsize_out = X25_DEFAULT_PACKET_SIZE; 57262306a36Sopenharmony_ci x25->facilities.throughput = 0; /* by default don't negotiate 57362306a36Sopenharmony_ci throughput */ 57462306a36Sopenharmony_ci x25->facilities.reverse = X25_DEFAULT_REVERSE; 57562306a36Sopenharmony_ci x25->dte_facilities.calling_len = 0; 57662306a36Sopenharmony_ci x25->dte_facilities.called_len = 0; 57762306a36Sopenharmony_ci memset(x25->dte_facilities.called_ae, '\0', 57862306a36Sopenharmony_ci sizeof(x25->dte_facilities.called_ae)); 57962306a36Sopenharmony_ci memset(x25->dte_facilities.calling_ae, '\0', 58062306a36Sopenharmony_ci sizeof(x25->dte_facilities.calling_ae)); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci rc = 0; 58362306a36Sopenharmony_ciout: 58462306a36Sopenharmony_ci return rc; 58562306a36Sopenharmony_ci} 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_cistatic struct sock *x25_make_new(struct sock *osk) 58862306a36Sopenharmony_ci{ 58962306a36Sopenharmony_ci struct sock *sk = NULL; 59062306a36Sopenharmony_ci struct x25_sock *x25, *ox25; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci if (osk->sk_type != SOCK_SEQPACKET) 59362306a36Sopenharmony_ci goto out; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci if ((sk = x25_alloc_socket(sock_net(osk), 0)) == NULL) 59662306a36Sopenharmony_ci goto out; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci x25 = x25_sk(sk); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci sk->sk_type = osk->sk_type; 60162306a36Sopenharmony_ci sk->sk_priority = osk->sk_priority; 60262306a36Sopenharmony_ci sk->sk_protocol = osk->sk_protocol; 60362306a36Sopenharmony_ci sk->sk_rcvbuf = osk->sk_rcvbuf; 60462306a36Sopenharmony_ci sk->sk_sndbuf = osk->sk_sndbuf; 60562306a36Sopenharmony_ci sk->sk_state = TCP_ESTABLISHED; 60662306a36Sopenharmony_ci sk->sk_backlog_rcv = osk->sk_backlog_rcv; 60762306a36Sopenharmony_ci sock_copy_flags(sk, osk); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci ox25 = x25_sk(osk); 61062306a36Sopenharmony_ci x25->t21 = ox25->t21; 61162306a36Sopenharmony_ci x25->t22 = ox25->t22; 61262306a36Sopenharmony_ci x25->t23 = ox25->t23; 61362306a36Sopenharmony_ci x25->t2 = ox25->t2; 61462306a36Sopenharmony_ci x25->flags = ox25->flags; 61562306a36Sopenharmony_ci x25->facilities = ox25->facilities; 61662306a36Sopenharmony_ci x25->dte_facilities = ox25->dte_facilities; 61762306a36Sopenharmony_ci x25->cudmatchlength = ox25->cudmatchlength; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci clear_bit(X25_INTERRUPT_FLAG, &x25->flags); 62062306a36Sopenharmony_ci x25_init_timers(sk); 62162306a36Sopenharmony_ciout: 62262306a36Sopenharmony_ci return sk; 62362306a36Sopenharmony_ci} 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_cistatic int x25_release(struct socket *sock) 62662306a36Sopenharmony_ci{ 62762306a36Sopenharmony_ci struct sock *sk = sock->sk; 62862306a36Sopenharmony_ci struct x25_sock *x25; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci if (!sk) 63162306a36Sopenharmony_ci return 0; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci x25 = x25_sk(sk); 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci sock_hold(sk); 63662306a36Sopenharmony_ci lock_sock(sk); 63762306a36Sopenharmony_ci switch (x25->state) { 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci case X25_STATE_0: 64062306a36Sopenharmony_ci case X25_STATE_2: 64162306a36Sopenharmony_ci x25_disconnect(sk, 0, 0, 0); 64262306a36Sopenharmony_ci __x25_destroy_socket(sk); 64362306a36Sopenharmony_ci goto out; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci case X25_STATE_1: 64662306a36Sopenharmony_ci case X25_STATE_3: 64762306a36Sopenharmony_ci case X25_STATE_4: 64862306a36Sopenharmony_ci x25_clear_queues(sk); 64962306a36Sopenharmony_ci x25_write_internal(sk, X25_CLEAR_REQUEST); 65062306a36Sopenharmony_ci x25_start_t23timer(sk); 65162306a36Sopenharmony_ci x25->state = X25_STATE_2; 65262306a36Sopenharmony_ci sk->sk_state = TCP_CLOSE; 65362306a36Sopenharmony_ci sk->sk_shutdown |= SEND_SHUTDOWN; 65462306a36Sopenharmony_ci sk->sk_state_change(sk); 65562306a36Sopenharmony_ci sock_set_flag(sk, SOCK_DEAD); 65662306a36Sopenharmony_ci sock_set_flag(sk, SOCK_DESTROY); 65762306a36Sopenharmony_ci break; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci case X25_STATE_5: 66062306a36Sopenharmony_ci x25_write_internal(sk, X25_CLEAR_REQUEST); 66162306a36Sopenharmony_ci x25_disconnect(sk, 0, 0, 0); 66262306a36Sopenharmony_ci __x25_destroy_socket(sk); 66362306a36Sopenharmony_ci goto out; 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci sock_orphan(sk); 66762306a36Sopenharmony_ciout: 66862306a36Sopenharmony_ci release_sock(sk); 66962306a36Sopenharmony_ci sock_put(sk); 67062306a36Sopenharmony_ci return 0; 67162306a36Sopenharmony_ci} 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_cistatic int x25_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) 67462306a36Sopenharmony_ci{ 67562306a36Sopenharmony_ci struct sock *sk = sock->sk; 67662306a36Sopenharmony_ci struct sockaddr_x25 *addr = (struct sockaddr_x25 *)uaddr; 67762306a36Sopenharmony_ci int len, i, rc = 0; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci if (addr_len != sizeof(struct sockaddr_x25) || 68062306a36Sopenharmony_ci addr->sx25_family != AF_X25 || 68162306a36Sopenharmony_ci strnlen(addr->sx25_addr.x25_addr, X25_ADDR_LEN) == X25_ADDR_LEN) { 68262306a36Sopenharmony_ci rc = -EINVAL; 68362306a36Sopenharmony_ci goto out; 68462306a36Sopenharmony_ci } 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci /* check for the null_x25_address */ 68762306a36Sopenharmony_ci if (strcmp(addr->sx25_addr.x25_addr, null_x25_address.x25_addr)) { 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci len = strlen(addr->sx25_addr.x25_addr); 69062306a36Sopenharmony_ci for (i = 0; i < len; i++) { 69162306a36Sopenharmony_ci if (!isdigit(addr->sx25_addr.x25_addr[i])) { 69262306a36Sopenharmony_ci rc = -EINVAL; 69362306a36Sopenharmony_ci goto out; 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci } 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci lock_sock(sk); 69962306a36Sopenharmony_ci if (sock_flag(sk, SOCK_ZAPPED)) { 70062306a36Sopenharmony_ci x25_sk(sk)->source_addr = addr->sx25_addr; 70162306a36Sopenharmony_ci x25_insert_socket(sk); 70262306a36Sopenharmony_ci sock_reset_flag(sk, SOCK_ZAPPED); 70362306a36Sopenharmony_ci } else { 70462306a36Sopenharmony_ci rc = -EINVAL; 70562306a36Sopenharmony_ci } 70662306a36Sopenharmony_ci release_sock(sk); 70762306a36Sopenharmony_ci SOCK_DEBUG(sk, "x25_bind: socket is bound\n"); 70862306a36Sopenharmony_ciout: 70962306a36Sopenharmony_ci return rc; 71062306a36Sopenharmony_ci} 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_cistatic int x25_wait_for_connection_establishment(struct sock *sk) 71362306a36Sopenharmony_ci{ 71462306a36Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 71562306a36Sopenharmony_ci int rc; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci add_wait_queue_exclusive(sk_sleep(sk), &wait); 71862306a36Sopenharmony_ci for (;;) { 71962306a36Sopenharmony_ci __set_current_state(TASK_INTERRUPTIBLE); 72062306a36Sopenharmony_ci rc = -ERESTARTSYS; 72162306a36Sopenharmony_ci if (signal_pending(current)) 72262306a36Sopenharmony_ci break; 72362306a36Sopenharmony_ci rc = sock_error(sk); 72462306a36Sopenharmony_ci if (rc) { 72562306a36Sopenharmony_ci sk->sk_socket->state = SS_UNCONNECTED; 72662306a36Sopenharmony_ci break; 72762306a36Sopenharmony_ci } 72862306a36Sopenharmony_ci rc = -ENOTCONN; 72962306a36Sopenharmony_ci if (sk->sk_state == TCP_CLOSE) { 73062306a36Sopenharmony_ci sk->sk_socket->state = SS_UNCONNECTED; 73162306a36Sopenharmony_ci break; 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci rc = 0; 73462306a36Sopenharmony_ci if (sk->sk_state != TCP_ESTABLISHED) { 73562306a36Sopenharmony_ci release_sock(sk); 73662306a36Sopenharmony_ci schedule(); 73762306a36Sopenharmony_ci lock_sock(sk); 73862306a36Sopenharmony_ci } else 73962306a36Sopenharmony_ci break; 74062306a36Sopenharmony_ci } 74162306a36Sopenharmony_ci __set_current_state(TASK_RUNNING); 74262306a36Sopenharmony_ci remove_wait_queue(sk_sleep(sk), &wait); 74362306a36Sopenharmony_ci return rc; 74462306a36Sopenharmony_ci} 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_cistatic int x25_connect(struct socket *sock, struct sockaddr *uaddr, 74762306a36Sopenharmony_ci int addr_len, int flags) 74862306a36Sopenharmony_ci{ 74962306a36Sopenharmony_ci struct sock *sk = sock->sk; 75062306a36Sopenharmony_ci struct x25_sock *x25 = x25_sk(sk); 75162306a36Sopenharmony_ci struct sockaddr_x25 *addr = (struct sockaddr_x25 *)uaddr; 75262306a36Sopenharmony_ci struct x25_route *rt; 75362306a36Sopenharmony_ci int rc = 0; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci lock_sock(sk); 75662306a36Sopenharmony_ci if (sk->sk_state == TCP_ESTABLISHED && sock->state == SS_CONNECTING) { 75762306a36Sopenharmony_ci sock->state = SS_CONNECTED; 75862306a36Sopenharmony_ci goto out; /* Connect completed during a ERESTARTSYS event */ 75962306a36Sopenharmony_ci } 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci rc = -ECONNREFUSED; 76262306a36Sopenharmony_ci if (sk->sk_state == TCP_CLOSE && sock->state == SS_CONNECTING) { 76362306a36Sopenharmony_ci sock->state = SS_UNCONNECTED; 76462306a36Sopenharmony_ci goto out; 76562306a36Sopenharmony_ci } 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci rc = -EISCONN; /* No reconnect on a seqpacket socket */ 76862306a36Sopenharmony_ci if (sk->sk_state == TCP_ESTABLISHED) 76962306a36Sopenharmony_ci goto out; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci rc = -EALREADY; /* Do nothing if call is already in progress */ 77262306a36Sopenharmony_ci if (sk->sk_state == TCP_SYN_SENT) 77362306a36Sopenharmony_ci goto out; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci sk->sk_state = TCP_CLOSE; 77662306a36Sopenharmony_ci sock->state = SS_UNCONNECTED; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci rc = -EINVAL; 77962306a36Sopenharmony_ci if (addr_len != sizeof(struct sockaddr_x25) || 78062306a36Sopenharmony_ci addr->sx25_family != AF_X25 || 78162306a36Sopenharmony_ci strnlen(addr->sx25_addr.x25_addr, X25_ADDR_LEN) == X25_ADDR_LEN) 78262306a36Sopenharmony_ci goto out; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci rc = -ENETUNREACH; 78562306a36Sopenharmony_ci rt = x25_get_route(&addr->sx25_addr); 78662306a36Sopenharmony_ci if (!rt) 78762306a36Sopenharmony_ci goto out; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci x25->neighbour = x25_get_neigh(rt->dev); 79062306a36Sopenharmony_ci if (!x25->neighbour) 79162306a36Sopenharmony_ci goto out_put_route; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci x25_limit_facilities(&x25->facilities, x25->neighbour); 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci x25->lci = x25_new_lci(x25->neighbour); 79662306a36Sopenharmony_ci if (!x25->lci) 79762306a36Sopenharmony_ci goto out_put_neigh; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci rc = -EINVAL; 80062306a36Sopenharmony_ci if (sock_flag(sk, SOCK_ZAPPED)) /* Must bind first - autobinding does not work */ 80162306a36Sopenharmony_ci goto out_put_neigh; 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci if (!strcmp(x25->source_addr.x25_addr, null_x25_address.x25_addr)) 80462306a36Sopenharmony_ci memset(&x25->source_addr, '\0', X25_ADDR_LEN); 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci x25->dest_addr = addr->sx25_addr; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci /* Move to connecting socket, start sending Connect Requests */ 80962306a36Sopenharmony_ci sock->state = SS_CONNECTING; 81062306a36Sopenharmony_ci sk->sk_state = TCP_SYN_SENT; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci x25->state = X25_STATE_1; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci x25_write_internal(sk, X25_CALL_REQUEST); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci x25_start_heartbeat(sk); 81762306a36Sopenharmony_ci x25_start_t21timer(sk); 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci /* Now the loop */ 82062306a36Sopenharmony_ci rc = -EINPROGRESS; 82162306a36Sopenharmony_ci if (sk->sk_state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) 82262306a36Sopenharmony_ci goto out; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci rc = x25_wait_for_connection_establishment(sk); 82562306a36Sopenharmony_ci if (rc) 82662306a36Sopenharmony_ci goto out_put_neigh; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci sock->state = SS_CONNECTED; 82962306a36Sopenharmony_ci rc = 0; 83062306a36Sopenharmony_ciout_put_neigh: 83162306a36Sopenharmony_ci if (rc && x25->neighbour) { 83262306a36Sopenharmony_ci read_lock_bh(&x25_list_lock); 83362306a36Sopenharmony_ci x25_neigh_put(x25->neighbour); 83462306a36Sopenharmony_ci x25->neighbour = NULL; 83562306a36Sopenharmony_ci read_unlock_bh(&x25_list_lock); 83662306a36Sopenharmony_ci x25->state = X25_STATE_0; 83762306a36Sopenharmony_ci } 83862306a36Sopenharmony_ciout_put_route: 83962306a36Sopenharmony_ci x25_route_put(rt); 84062306a36Sopenharmony_ciout: 84162306a36Sopenharmony_ci release_sock(sk); 84262306a36Sopenharmony_ci return rc; 84362306a36Sopenharmony_ci} 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_cistatic int x25_wait_for_data(struct sock *sk, long timeout) 84662306a36Sopenharmony_ci{ 84762306a36Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 84862306a36Sopenharmony_ci int rc = 0; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci add_wait_queue_exclusive(sk_sleep(sk), &wait); 85162306a36Sopenharmony_ci for (;;) { 85262306a36Sopenharmony_ci __set_current_state(TASK_INTERRUPTIBLE); 85362306a36Sopenharmony_ci if (sk->sk_shutdown & RCV_SHUTDOWN) 85462306a36Sopenharmony_ci break; 85562306a36Sopenharmony_ci rc = -ERESTARTSYS; 85662306a36Sopenharmony_ci if (signal_pending(current)) 85762306a36Sopenharmony_ci break; 85862306a36Sopenharmony_ci rc = -EAGAIN; 85962306a36Sopenharmony_ci if (!timeout) 86062306a36Sopenharmony_ci break; 86162306a36Sopenharmony_ci rc = 0; 86262306a36Sopenharmony_ci if (skb_queue_empty(&sk->sk_receive_queue)) { 86362306a36Sopenharmony_ci release_sock(sk); 86462306a36Sopenharmony_ci timeout = schedule_timeout(timeout); 86562306a36Sopenharmony_ci lock_sock(sk); 86662306a36Sopenharmony_ci } else 86762306a36Sopenharmony_ci break; 86862306a36Sopenharmony_ci } 86962306a36Sopenharmony_ci __set_current_state(TASK_RUNNING); 87062306a36Sopenharmony_ci remove_wait_queue(sk_sleep(sk), &wait); 87162306a36Sopenharmony_ci return rc; 87262306a36Sopenharmony_ci} 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_cistatic int x25_accept(struct socket *sock, struct socket *newsock, int flags, 87562306a36Sopenharmony_ci bool kern) 87662306a36Sopenharmony_ci{ 87762306a36Sopenharmony_ci struct sock *sk = sock->sk; 87862306a36Sopenharmony_ci struct sock *newsk; 87962306a36Sopenharmony_ci struct sk_buff *skb; 88062306a36Sopenharmony_ci int rc = -EINVAL; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci if (!sk) 88362306a36Sopenharmony_ci goto out; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci rc = -EOPNOTSUPP; 88662306a36Sopenharmony_ci if (sk->sk_type != SOCK_SEQPACKET) 88762306a36Sopenharmony_ci goto out; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci lock_sock(sk); 89062306a36Sopenharmony_ci rc = -EINVAL; 89162306a36Sopenharmony_ci if (sk->sk_state != TCP_LISTEN) 89262306a36Sopenharmony_ci goto out2; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci rc = x25_wait_for_data(sk, sk->sk_rcvtimeo); 89562306a36Sopenharmony_ci if (rc) 89662306a36Sopenharmony_ci goto out2; 89762306a36Sopenharmony_ci skb = skb_dequeue(&sk->sk_receive_queue); 89862306a36Sopenharmony_ci rc = -EINVAL; 89962306a36Sopenharmony_ci if (!skb->sk) 90062306a36Sopenharmony_ci goto out2; 90162306a36Sopenharmony_ci newsk = skb->sk; 90262306a36Sopenharmony_ci sock_graft(newsk, newsock); 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci /* Now attach up the new socket */ 90562306a36Sopenharmony_ci skb->sk = NULL; 90662306a36Sopenharmony_ci kfree_skb(skb); 90762306a36Sopenharmony_ci sk_acceptq_removed(sk); 90862306a36Sopenharmony_ci newsock->state = SS_CONNECTED; 90962306a36Sopenharmony_ci rc = 0; 91062306a36Sopenharmony_ciout2: 91162306a36Sopenharmony_ci release_sock(sk); 91262306a36Sopenharmony_ciout: 91362306a36Sopenharmony_ci return rc; 91462306a36Sopenharmony_ci} 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_cistatic int x25_getname(struct socket *sock, struct sockaddr *uaddr, 91762306a36Sopenharmony_ci int peer) 91862306a36Sopenharmony_ci{ 91962306a36Sopenharmony_ci struct sockaddr_x25 *sx25 = (struct sockaddr_x25 *)uaddr; 92062306a36Sopenharmony_ci struct sock *sk = sock->sk; 92162306a36Sopenharmony_ci struct x25_sock *x25 = x25_sk(sk); 92262306a36Sopenharmony_ci int rc = 0; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci if (peer) { 92562306a36Sopenharmony_ci if (sk->sk_state != TCP_ESTABLISHED) { 92662306a36Sopenharmony_ci rc = -ENOTCONN; 92762306a36Sopenharmony_ci goto out; 92862306a36Sopenharmony_ci } 92962306a36Sopenharmony_ci sx25->sx25_addr = x25->dest_addr; 93062306a36Sopenharmony_ci } else 93162306a36Sopenharmony_ci sx25->sx25_addr = x25->source_addr; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci sx25->sx25_family = AF_X25; 93462306a36Sopenharmony_ci rc = sizeof(*sx25); 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ciout: 93762306a36Sopenharmony_ci return rc; 93862306a36Sopenharmony_ci} 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ciint x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *nb, 94162306a36Sopenharmony_ci unsigned int lci) 94262306a36Sopenharmony_ci{ 94362306a36Sopenharmony_ci struct sock *sk; 94462306a36Sopenharmony_ci struct sock *make; 94562306a36Sopenharmony_ci struct x25_sock *makex25; 94662306a36Sopenharmony_ci struct x25_address source_addr, dest_addr; 94762306a36Sopenharmony_ci struct x25_facilities facilities; 94862306a36Sopenharmony_ci struct x25_dte_facilities dte_facilities; 94962306a36Sopenharmony_ci int len, addr_len, rc; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci /* 95262306a36Sopenharmony_ci * Remove the LCI and frame type. 95362306a36Sopenharmony_ci */ 95462306a36Sopenharmony_ci skb_pull(skb, X25_STD_MIN_LEN); 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci /* 95762306a36Sopenharmony_ci * Extract the X.25 addresses and convert them to ASCII strings, 95862306a36Sopenharmony_ci * and remove them. 95962306a36Sopenharmony_ci * 96062306a36Sopenharmony_ci * Address block is mandatory in call request packets 96162306a36Sopenharmony_ci */ 96262306a36Sopenharmony_ci addr_len = x25_parse_address_block(skb, &source_addr, &dest_addr); 96362306a36Sopenharmony_ci if (addr_len <= 0) 96462306a36Sopenharmony_ci goto out_clear_request; 96562306a36Sopenharmony_ci skb_pull(skb, addr_len); 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci /* 96862306a36Sopenharmony_ci * Get the length of the facilities, skip past them for the moment 96962306a36Sopenharmony_ci * get the call user data because this is needed to determine 97062306a36Sopenharmony_ci * the correct listener 97162306a36Sopenharmony_ci * 97262306a36Sopenharmony_ci * Facilities length is mandatory in call request packets 97362306a36Sopenharmony_ci */ 97462306a36Sopenharmony_ci if (!pskb_may_pull(skb, 1)) 97562306a36Sopenharmony_ci goto out_clear_request; 97662306a36Sopenharmony_ci len = skb->data[0] + 1; 97762306a36Sopenharmony_ci if (!pskb_may_pull(skb, len)) 97862306a36Sopenharmony_ci goto out_clear_request; 97962306a36Sopenharmony_ci skb_pull(skb,len); 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci /* 98262306a36Sopenharmony_ci * Ensure that the amount of call user data is valid. 98362306a36Sopenharmony_ci */ 98462306a36Sopenharmony_ci if (skb->len > X25_MAX_CUD_LEN) 98562306a36Sopenharmony_ci goto out_clear_request; 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci /* 98862306a36Sopenharmony_ci * Get all the call user data so it can be used in 98962306a36Sopenharmony_ci * x25_find_listener and skb_copy_from_linear_data up ahead. 99062306a36Sopenharmony_ci */ 99162306a36Sopenharmony_ci if (!pskb_may_pull(skb, skb->len)) 99262306a36Sopenharmony_ci goto out_clear_request; 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci /* 99562306a36Sopenharmony_ci * Find a listener for the particular address/cud pair. 99662306a36Sopenharmony_ci */ 99762306a36Sopenharmony_ci sk = x25_find_listener(&source_addr,skb); 99862306a36Sopenharmony_ci skb_push(skb,len); 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci if (sk != NULL && sk_acceptq_is_full(sk)) { 100162306a36Sopenharmony_ci goto out_sock_put; 100262306a36Sopenharmony_ci } 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci /* 100562306a36Sopenharmony_ci * We dont have any listeners for this incoming call. 100662306a36Sopenharmony_ci * Try forwarding it. 100762306a36Sopenharmony_ci */ 100862306a36Sopenharmony_ci if (sk == NULL) { 100962306a36Sopenharmony_ci skb_push(skb, addr_len + X25_STD_MIN_LEN); 101062306a36Sopenharmony_ci if (sysctl_x25_forward && 101162306a36Sopenharmony_ci x25_forward_call(&dest_addr, nb, skb, lci) > 0) 101262306a36Sopenharmony_ci { 101362306a36Sopenharmony_ci /* Call was forwarded, dont process it any more */ 101462306a36Sopenharmony_ci kfree_skb(skb); 101562306a36Sopenharmony_ci rc = 1; 101662306a36Sopenharmony_ci goto out; 101762306a36Sopenharmony_ci } else { 101862306a36Sopenharmony_ci /* No listeners, can't forward, clear the call */ 101962306a36Sopenharmony_ci goto out_clear_request; 102062306a36Sopenharmony_ci } 102162306a36Sopenharmony_ci } 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci /* 102462306a36Sopenharmony_ci * Try to reach a compromise on the requested facilities. 102562306a36Sopenharmony_ci */ 102662306a36Sopenharmony_ci len = x25_negotiate_facilities(skb, sk, &facilities, &dte_facilities); 102762306a36Sopenharmony_ci if (len == -1) 102862306a36Sopenharmony_ci goto out_sock_put; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci /* 103162306a36Sopenharmony_ci * current neighbour/link might impose additional limits 103262306a36Sopenharmony_ci * on certain facilities 103362306a36Sopenharmony_ci */ 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci x25_limit_facilities(&facilities, nb); 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci /* 103862306a36Sopenharmony_ci * Try to create a new socket. 103962306a36Sopenharmony_ci */ 104062306a36Sopenharmony_ci make = x25_make_new(sk); 104162306a36Sopenharmony_ci if (!make) 104262306a36Sopenharmony_ci goto out_sock_put; 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci /* 104562306a36Sopenharmony_ci * Remove the facilities 104662306a36Sopenharmony_ci */ 104762306a36Sopenharmony_ci skb_pull(skb, len); 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci skb->sk = make; 105062306a36Sopenharmony_ci make->sk_state = TCP_ESTABLISHED; 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci makex25 = x25_sk(make); 105362306a36Sopenharmony_ci makex25->lci = lci; 105462306a36Sopenharmony_ci makex25->dest_addr = dest_addr; 105562306a36Sopenharmony_ci makex25->source_addr = source_addr; 105662306a36Sopenharmony_ci x25_neigh_hold(nb); 105762306a36Sopenharmony_ci makex25->neighbour = nb; 105862306a36Sopenharmony_ci makex25->facilities = facilities; 105962306a36Sopenharmony_ci makex25->dte_facilities= dte_facilities; 106062306a36Sopenharmony_ci makex25->vc_facil_mask = x25_sk(sk)->vc_facil_mask; 106162306a36Sopenharmony_ci /* ensure no reverse facil on accept */ 106262306a36Sopenharmony_ci makex25->vc_facil_mask &= ~X25_MASK_REVERSE; 106362306a36Sopenharmony_ci /* ensure no calling address extension on accept */ 106462306a36Sopenharmony_ci makex25->vc_facil_mask &= ~X25_MASK_CALLING_AE; 106562306a36Sopenharmony_ci makex25->cudmatchlength = x25_sk(sk)->cudmatchlength; 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci /* Normally all calls are accepted immediately */ 106862306a36Sopenharmony_ci if (test_bit(X25_ACCPT_APPRV_FLAG, &makex25->flags)) { 106962306a36Sopenharmony_ci x25_write_internal(make, X25_CALL_ACCEPTED); 107062306a36Sopenharmony_ci makex25->state = X25_STATE_3; 107162306a36Sopenharmony_ci } else { 107262306a36Sopenharmony_ci makex25->state = X25_STATE_5; 107362306a36Sopenharmony_ci } 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci /* 107662306a36Sopenharmony_ci * Incoming Call User Data. 107762306a36Sopenharmony_ci */ 107862306a36Sopenharmony_ci skb_copy_from_linear_data(skb, makex25->calluserdata.cuddata, skb->len); 107962306a36Sopenharmony_ci makex25->calluserdata.cudlength = skb->len; 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci sk_acceptq_added(sk); 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci x25_insert_socket(make); 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci skb_queue_head(&sk->sk_receive_queue, skb); 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci x25_start_heartbeat(make); 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci if (!sock_flag(sk, SOCK_DEAD)) 109062306a36Sopenharmony_ci sk->sk_data_ready(sk); 109162306a36Sopenharmony_ci rc = 1; 109262306a36Sopenharmony_ci sock_put(sk); 109362306a36Sopenharmony_ciout: 109462306a36Sopenharmony_ci return rc; 109562306a36Sopenharmony_ciout_sock_put: 109662306a36Sopenharmony_ci sock_put(sk); 109762306a36Sopenharmony_ciout_clear_request: 109862306a36Sopenharmony_ci rc = 0; 109962306a36Sopenharmony_ci x25_transmit_clear_request(nb, lci, 0x01); 110062306a36Sopenharmony_ci goto out; 110162306a36Sopenharmony_ci} 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_cistatic int x25_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) 110462306a36Sopenharmony_ci{ 110562306a36Sopenharmony_ci struct sock *sk = sock->sk; 110662306a36Sopenharmony_ci struct x25_sock *x25 = x25_sk(sk); 110762306a36Sopenharmony_ci DECLARE_SOCKADDR(struct sockaddr_x25 *, usx25, msg->msg_name); 110862306a36Sopenharmony_ci struct sockaddr_x25 sx25; 110962306a36Sopenharmony_ci struct sk_buff *skb; 111062306a36Sopenharmony_ci unsigned char *asmptr; 111162306a36Sopenharmony_ci int noblock = msg->msg_flags & MSG_DONTWAIT; 111262306a36Sopenharmony_ci size_t size; 111362306a36Sopenharmony_ci int qbit = 0, rc = -EINVAL; 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci lock_sock(sk); 111662306a36Sopenharmony_ci if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_OOB|MSG_EOR|MSG_CMSG_COMPAT)) 111762306a36Sopenharmony_ci goto out; 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci /* we currently don't support segmented records at the user interface */ 112062306a36Sopenharmony_ci if (!(msg->msg_flags & (MSG_EOR|MSG_OOB))) 112162306a36Sopenharmony_ci goto out; 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci rc = -EADDRNOTAVAIL; 112462306a36Sopenharmony_ci if (sock_flag(sk, SOCK_ZAPPED)) 112562306a36Sopenharmony_ci goto out; 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci rc = -EPIPE; 112862306a36Sopenharmony_ci if (sk->sk_shutdown & SEND_SHUTDOWN) { 112962306a36Sopenharmony_ci send_sig(SIGPIPE, current, 0); 113062306a36Sopenharmony_ci goto out; 113162306a36Sopenharmony_ci } 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci rc = -ENETUNREACH; 113462306a36Sopenharmony_ci if (!x25->neighbour) 113562306a36Sopenharmony_ci goto out; 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci if (usx25) { 113862306a36Sopenharmony_ci rc = -EINVAL; 113962306a36Sopenharmony_ci if (msg->msg_namelen < sizeof(sx25)) 114062306a36Sopenharmony_ci goto out; 114162306a36Sopenharmony_ci memcpy(&sx25, usx25, sizeof(sx25)); 114262306a36Sopenharmony_ci rc = -EISCONN; 114362306a36Sopenharmony_ci if (strcmp(x25->dest_addr.x25_addr, sx25.sx25_addr.x25_addr)) 114462306a36Sopenharmony_ci goto out; 114562306a36Sopenharmony_ci rc = -EINVAL; 114662306a36Sopenharmony_ci if (sx25.sx25_family != AF_X25) 114762306a36Sopenharmony_ci goto out; 114862306a36Sopenharmony_ci } else { 114962306a36Sopenharmony_ci /* 115062306a36Sopenharmony_ci * FIXME 1003.1g - if the socket is like this because 115162306a36Sopenharmony_ci * it has become closed (not started closed) we ought 115262306a36Sopenharmony_ci * to SIGPIPE, EPIPE; 115362306a36Sopenharmony_ci */ 115462306a36Sopenharmony_ci rc = -ENOTCONN; 115562306a36Sopenharmony_ci if (sk->sk_state != TCP_ESTABLISHED) 115662306a36Sopenharmony_ci goto out; 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci sx25.sx25_family = AF_X25; 115962306a36Sopenharmony_ci sx25.sx25_addr = x25->dest_addr; 116062306a36Sopenharmony_ci } 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci /* Sanity check the packet size */ 116362306a36Sopenharmony_ci if (len > 65535) { 116462306a36Sopenharmony_ci rc = -EMSGSIZE; 116562306a36Sopenharmony_ci goto out; 116662306a36Sopenharmony_ci } 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci SOCK_DEBUG(sk, "x25_sendmsg: sendto: Addresses built.\n"); 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci /* Build a packet */ 117162306a36Sopenharmony_ci SOCK_DEBUG(sk, "x25_sendmsg: sendto: building packet.\n"); 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci if ((msg->msg_flags & MSG_OOB) && len > 32) 117462306a36Sopenharmony_ci len = 32; 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci size = len + X25_MAX_L2_LEN + X25_EXT_MIN_LEN; 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci release_sock(sk); 117962306a36Sopenharmony_ci skb = sock_alloc_send_skb(sk, size, noblock, &rc); 118062306a36Sopenharmony_ci lock_sock(sk); 118162306a36Sopenharmony_ci if (!skb) 118262306a36Sopenharmony_ci goto out; 118362306a36Sopenharmony_ci X25_SKB_CB(skb)->flags = msg->msg_flags; 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci skb_reserve(skb, X25_MAX_L2_LEN + X25_EXT_MIN_LEN); 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci /* 118862306a36Sopenharmony_ci * Put the data on the end 118962306a36Sopenharmony_ci */ 119062306a36Sopenharmony_ci SOCK_DEBUG(sk, "x25_sendmsg: Copying user data\n"); 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci skb_reset_transport_header(skb); 119362306a36Sopenharmony_ci skb_put(skb, len); 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci rc = memcpy_from_msg(skb_transport_header(skb), msg, len); 119662306a36Sopenharmony_ci if (rc) 119762306a36Sopenharmony_ci goto out_kfree_skb; 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci /* 120062306a36Sopenharmony_ci * If the Q BIT Include socket option is in force, the first 120162306a36Sopenharmony_ci * byte of the user data is the logical value of the Q Bit. 120262306a36Sopenharmony_ci */ 120362306a36Sopenharmony_ci if (test_bit(X25_Q_BIT_FLAG, &x25->flags)) { 120462306a36Sopenharmony_ci if (!pskb_may_pull(skb, 1)) 120562306a36Sopenharmony_ci goto out_kfree_skb; 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci qbit = skb->data[0]; 120862306a36Sopenharmony_ci skb_pull(skb, 1); 120962306a36Sopenharmony_ci } 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci /* 121262306a36Sopenharmony_ci * Push down the X.25 header 121362306a36Sopenharmony_ci */ 121462306a36Sopenharmony_ci SOCK_DEBUG(sk, "x25_sendmsg: Building X.25 Header.\n"); 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci if (msg->msg_flags & MSG_OOB) { 121762306a36Sopenharmony_ci if (x25->neighbour->extended) { 121862306a36Sopenharmony_ci asmptr = skb_push(skb, X25_STD_MIN_LEN); 121962306a36Sopenharmony_ci *asmptr++ = ((x25->lci >> 8) & 0x0F) | X25_GFI_EXTSEQ; 122062306a36Sopenharmony_ci *asmptr++ = (x25->lci >> 0) & 0xFF; 122162306a36Sopenharmony_ci *asmptr++ = X25_INTERRUPT; 122262306a36Sopenharmony_ci } else { 122362306a36Sopenharmony_ci asmptr = skb_push(skb, X25_STD_MIN_LEN); 122462306a36Sopenharmony_ci *asmptr++ = ((x25->lci >> 8) & 0x0F) | X25_GFI_STDSEQ; 122562306a36Sopenharmony_ci *asmptr++ = (x25->lci >> 0) & 0xFF; 122662306a36Sopenharmony_ci *asmptr++ = X25_INTERRUPT; 122762306a36Sopenharmony_ci } 122862306a36Sopenharmony_ci } else { 122962306a36Sopenharmony_ci if (x25->neighbour->extended) { 123062306a36Sopenharmony_ci /* Build an Extended X.25 header */ 123162306a36Sopenharmony_ci asmptr = skb_push(skb, X25_EXT_MIN_LEN); 123262306a36Sopenharmony_ci *asmptr++ = ((x25->lci >> 8) & 0x0F) | X25_GFI_EXTSEQ; 123362306a36Sopenharmony_ci *asmptr++ = (x25->lci >> 0) & 0xFF; 123462306a36Sopenharmony_ci *asmptr++ = X25_DATA; 123562306a36Sopenharmony_ci *asmptr++ = X25_DATA; 123662306a36Sopenharmony_ci } else { 123762306a36Sopenharmony_ci /* Build an Standard X.25 header */ 123862306a36Sopenharmony_ci asmptr = skb_push(skb, X25_STD_MIN_LEN); 123962306a36Sopenharmony_ci *asmptr++ = ((x25->lci >> 8) & 0x0F) | X25_GFI_STDSEQ; 124062306a36Sopenharmony_ci *asmptr++ = (x25->lci >> 0) & 0xFF; 124162306a36Sopenharmony_ci *asmptr++ = X25_DATA; 124262306a36Sopenharmony_ci } 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci if (qbit) 124562306a36Sopenharmony_ci skb->data[0] |= X25_Q_BIT; 124662306a36Sopenharmony_ci } 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci SOCK_DEBUG(sk, "x25_sendmsg: Built header.\n"); 124962306a36Sopenharmony_ci SOCK_DEBUG(sk, "x25_sendmsg: Transmitting buffer\n"); 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci rc = -ENOTCONN; 125262306a36Sopenharmony_ci if (sk->sk_state != TCP_ESTABLISHED) 125362306a36Sopenharmony_ci goto out_kfree_skb; 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci if (msg->msg_flags & MSG_OOB) 125662306a36Sopenharmony_ci skb_queue_tail(&x25->interrupt_out_queue, skb); 125762306a36Sopenharmony_ci else { 125862306a36Sopenharmony_ci rc = x25_output(sk, skb); 125962306a36Sopenharmony_ci len = rc; 126062306a36Sopenharmony_ci if (rc < 0) 126162306a36Sopenharmony_ci kfree_skb(skb); 126262306a36Sopenharmony_ci else if (test_bit(X25_Q_BIT_FLAG, &x25->flags)) 126362306a36Sopenharmony_ci len++; 126462306a36Sopenharmony_ci } 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci x25_kick(sk); 126762306a36Sopenharmony_ci rc = len; 126862306a36Sopenharmony_ciout: 126962306a36Sopenharmony_ci release_sock(sk); 127062306a36Sopenharmony_ci return rc; 127162306a36Sopenharmony_ciout_kfree_skb: 127262306a36Sopenharmony_ci kfree_skb(skb); 127362306a36Sopenharmony_ci goto out; 127462306a36Sopenharmony_ci} 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_cistatic int x25_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, 127862306a36Sopenharmony_ci int flags) 127962306a36Sopenharmony_ci{ 128062306a36Sopenharmony_ci struct sock *sk = sock->sk; 128162306a36Sopenharmony_ci struct x25_sock *x25 = x25_sk(sk); 128262306a36Sopenharmony_ci DECLARE_SOCKADDR(struct sockaddr_x25 *, sx25, msg->msg_name); 128362306a36Sopenharmony_ci size_t copied; 128462306a36Sopenharmony_ci int qbit, header_len; 128562306a36Sopenharmony_ci struct sk_buff *skb; 128662306a36Sopenharmony_ci unsigned char *asmptr; 128762306a36Sopenharmony_ci int rc = -ENOTCONN; 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci lock_sock(sk); 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci if (x25->neighbour == NULL) 129262306a36Sopenharmony_ci goto out; 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci header_len = x25->neighbour->extended ? 129562306a36Sopenharmony_ci X25_EXT_MIN_LEN : X25_STD_MIN_LEN; 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci /* 129862306a36Sopenharmony_ci * This works for seqpacket too. The receiver has ordered the queue for 129962306a36Sopenharmony_ci * us! We do one quick check first though 130062306a36Sopenharmony_ci */ 130162306a36Sopenharmony_ci if (sk->sk_state != TCP_ESTABLISHED) 130262306a36Sopenharmony_ci goto out; 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci if (flags & MSG_OOB) { 130562306a36Sopenharmony_ci rc = -EINVAL; 130662306a36Sopenharmony_ci if (sock_flag(sk, SOCK_URGINLINE) || 130762306a36Sopenharmony_ci !skb_peek(&x25->interrupt_in_queue)) 130862306a36Sopenharmony_ci goto out; 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci skb = skb_dequeue(&x25->interrupt_in_queue); 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci if (!pskb_may_pull(skb, X25_STD_MIN_LEN)) 131362306a36Sopenharmony_ci goto out_free_dgram; 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci skb_pull(skb, X25_STD_MIN_LEN); 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci /* 131862306a36Sopenharmony_ci * No Q bit information on Interrupt data. 131962306a36Sopenharmony_ci */ 132062306a36Sopenharmony_ci if (test_bit(X25_Q_BIT_FLAG, &x25->flags)) { 132162306a36Sopenharmony_ci asmptr = skb_push(skb, 1); 132262306a36Sopenharmony_ci *asmptr = 0x00; 132362306a36Sopenharmony_ci } 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci msg->msg_flags |= MSG_OOB; 132662306a36Sopenharmony_ci } else { 132762306a36Sopenharmony_ci /* Now we can treat all alike */ 132862306a36Sopenharmony_ci release_sock(sk); 132962306a36Sopenharmony_ci skb = skb_recv_datagram(sk, flags, &rc); 133062306a36Sopenharmony_ci lock_sock(sk); 133162306a36Sopenharmony_ci if (!skb) 133262306a36Sopenharmony_ci goto out; 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci if (!pskb_may_pull(skb, header_len)) 133562306a36Sopenharmony_ci goto out_free_dgram; 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci qbit = (skb->data[0] & X25_Q_BIT) == X25_Q_BIT; 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci skb_pull(skb, header_len); 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_ci if (test_bit(X25_Q_BIT_FLAG, &x25->flags)) { 134262306a36Sopenharmony_ci asmptr = skb_push(skb, 1); 134362306a36Sopenharmony_ci *asmptr = qbit; 134462306a36Sopenharmony_ci } 134562306a36Sopenharmony_ci } 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_ci skb_reset_transport_header(skb); 134862306a36Sopenharmony_ci copied = skb->len; 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci if (copied > size) { 135162306a36Sopenharmony_ci copied = size; 135262306a36Sopenharmony_ci msg->msg_flags |= MSG_TRUNC; 135362306a36Sopenharmony_ci } 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci /* Currently, each datagram always contains a complete record */ 135662306a36Sopenharmony_ci msg->msg_flags |= MSG_EOR; 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci rc = skb_copy_datagram_msg(skb, 0, msg, copied); 135962306a36Sopenharmony_ci if (rc) 136062306a36Sopenharmony_ci goto out_free_dgram; 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci if (sx25) { 136362306a36Sopenharmony_ci sx25->sx25_family = AF_X25; 136462306a36Sopenharmony_ci sx25->sx25_addr = x25->dest_addr; 136562306a36Sopenharmony_ci msg->msg_namelen = sizeof(*sx25); 136662306a36Sopenharmony_ci } 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci x25_check_rbuf(sk); 136962306a36Sopenharmony_ci rc = copied; 137062306a36Sopenharmony_ciout_free_dgram: 137162306a36Sopenharmony_ci skb_free_datagram(sk, skb); 137262306a36Sopenharmony_ciout: 137362306a36Sopenharmony_ci release_sock(sk); 137462306a36Sopenharmony_ci return rc; 137562306a36Sopenharmony_ci} 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_cistatic int x25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) 137962306a36Sopenharmony_ci{ 138062306a36Sopenharmony_ci struct sock *sk = sock->sk; 138162306a36Sopenharmony_ci struct x25_sock *x25 = x25_sk(sk); 138262306a36Sopenharmony_ci void __user *argp = (void __user *)arg; 138362306a36Sopenharmony_ci int rc; 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci switch (cmd) { 138662306a36Sopenharmony_ci case TIOCOUTQ: { 138762306a36Sopenharmony_ci int amount; 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk); 139062306a36Sopenharmony_ci if (amount < 0) 139162306a36Sopenharmony_ci amount = 0; 139262306a36Sopenharmony_ci rc = put_user(amount, (unsigned int __user *)argp); 139362306a36Sopenharmony_ci break; 139462306a36Sopenharmony_ci } 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci case TIOCINQ: { 139762306a36Sopenharmony_ci struct sk_buff *skb; 139862306a36Sopenharmony_ci int amount = 0; 139962306a36Sopenharmony_ci /* 140062306a36Sopenharmony_ci * These two are safe on a single CPU system as 140162306a36Sopenharmony_ci * only user tasks fiddle here 140262306a36Sopenharmony_ci */ 140362306a36Sopenharmony_ci lock_sock(sk); 140462306a36Sopenharmony_ci if ((skb = skb_peek(&sk->sk_receive_queue)) != NULL) 140562306a36Sopenharmony_ci amount = skb->len; 140662306a36Sopenharmony_ci release_sock(sk); 140762306a36Sopenharmony_ci rc = put_user(amount, (unsigned int __user *)argp); 140862306a36Sopenharmony_ci break; 140962306a36Sopenharmony_ci } 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_ci case SIOCGIFADDR: 141262306a36Sopenharmony_ci case SIOCSIFADDR: 141362306a36Sopenharmony_ci case SIOCGIFDSTADDR: 141462306a36Sopenharmony_ci case SIOCSIFDSTADDR: 141562306a36Sopenharmony_ci case SIOCGIFBRDADDR: 141662306a36Sopenharmony_ci case SIOCSIFBRDADDR: 141762306a36Sopenharmony_ci case SIOCGIFNETMASK: 141862306a36Sopenharmony_ci case SIOCSIFNETMASK: 141962306a36Sopenharmony_ci case SIOCGIFMETRIC: 142062306a36Sopenharmony_ci case SIOCSIFMETRIC: 142162306a36Sopenharmony_ci rc = -EINVAL; 142262306a36Sopenharmony_ci break; 142362306a36Sopenharmony_ci case SIOCADDRT: 142462306a36Sopenharmony_ci case SIOCDELRT: 142562306a36Sopenharmony_ci rc = -EPERM; 142662306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 142762306a36Sopenharmony_ci break; 142862306a36Sopenharmony_ci rc = x25_route_ioctl(cmd, argp); 142962306a36Sopenharmony_ci break; 143062306a36Sopenharmony_ci case SIOCX25GSUBSCRIP: 143162306a36Sopenharmony_ci rc = x25_subscr_ioctl(cmd, argp); 143262306a36Sopenharmony_ci break; 143362306a36Sopenharmony_ci case SIOCX25SSUBSCRIP: 143462306a36Sopenharmony_ci rc = -EPERM; 143562306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 143662306a36Sopenharmony_ci break; 143762306a36Sopenharmony_ci rc = x25_subscr_ioctl(cmd, argp); 143862306a36Sopenharmony_ci break; 143962306a36Sopenharmony_ci case SIOCX25GFACILITIES: { 144062306a36Sopenharmony_ci lock_sock(sk); 144162306a36Sopenharmony_ci rc = copy_to_user(argp, &x25->facilities, 144262306a36Sopenharmony_ci sizeof(x25->facilities)) 144362306a36Sopenharmony_ci ? -EFAULT : 0; 144462306a36Sopenharmony_ci release_sock(sk); 144562306a36Sopenharmony_ci break; 144662306a36Sopenharmony_ci } 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci case SIOCX25SFACILITIES: { 144962306a36Sopenharmony_ci struct x25_facilities facilities; 145062306a36Sopenharmony_ci rc = -EFAULT; 145162306a36Sopenharmony_ci if (copy_from_user(&facilities, argp, sizeof(facilities))) 145262306a36Sopenharmony_ci break; 145362306a36Sopenharmony_ci rc = -EINVAL; 145462306a36Sopenharmony_ci lock_sock(sk); 145562306a36Sopenharmony_ci if (sk->sk_state != TCP_LISTEN && 145662306a36Sopenharmony_ci sk->sk_state != TCP_CLOSE) 145762306a36Sopenharmony_ci goto out_fac_release; 145862306a36Sopenharmony_ci if (facilities.pacsize_in < X25_PS16 || 145962306a36Sopenharmony_ci facilities.pacsize_in > X25_PS4096) 146062306a36Sopenharmony_ci goto out_fac_release; 146162306a36Sopenharmony_ci if (facilities.pacsize_out < X25_PS16 || 146262306a36Sopenharmony_ci facilities.pacsize_out > X25_PS4096) 146362306a36Sopenharmony_ci goto out_fac_release; 146462306a36Sopenharmony_ci if (facilities.winsize_in < 1 || 146562306a36Sopenharmony_ci facilities.winsize_in > 127) 146662306a36Sopenharmony_ci goto out_fac_release; 146762306a36Sopenharmony_ci if (facilities.throughput) { 146862306a36Sopenharmony_ci int out = facilities.throughput & 0xf0; 146962306a36Sopenharmony_ci int in = facilities.throughput & 0x0f; 147062306a36Sopenharmony_ci if (!out) 147162306a36Sopenharmony_ci facilities.throughput |= 147262306a36Sopenharmony_ci X25_DEFAULT_THROUGHPUT << 4; 147362306a36Sopenharmony_ci else if (out < 0x30 || out > 0xD0) 147462306a36Sopenharmony_ci goto out_fac_release; 147562306a36Sopenharmony_ci if (!in) 147662306a36Sopenharmony_ci facilities.throughput |= 147762306a36Sopenharmony_ci X25_DEFAULT_THROUGHPUT; 147862306a36Sopenharmony_ci else if (in < 0x03 || in > 0x0D) 147962306a36Sopenharmony_ci goto out_fac_release; 148062306a36Sopenharmony_ci } 148162306a36Sopenharmony_ci if (facilities.reverse && 148262306a36Sopenharmony_ci (facilities.reverse & 0x81) != 0x81) 148362306a36Sopenharmony_ci goto out_fac_release; 148462306a36Sopenharmony_ci x25->facilities = facilities; 148562306a36Sopenharmony_ci rc = 0; 148662306a36Sopenharmony_ciout_fac_release: 148762306a36Sopenharmony_ci release_sock(sk); 148862306a36Sopenharmony_ci break; 148962306a36Sopenharmony_ci } 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_ci case SIOCX25GDTEFACILITIES: { 149262306a36Sopenharmony_ci lock_sock(sk); 149362306a36Sopenharmony_ci rc = copy_to_user(argp, &x25->dte_facilities, 149462306a36Sopenharmony_ci sizeof(x25->dte_facilities)); 149562306a36Sopenharmony_ci release_sock(sk); 149662306a36Sopenharmony_ci if (rc) 149762306a36Sopenharmony_ci rc = -EFAULT; 149862306a36Sopenharmony_ci break; 149962306a36Sopenharmony_ci } 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci case SIOCX25SDTEFACILITIES: { 150262306a36Sopenharmony_ci struct x25_dte_facilities dtefacs; 150362306a36Sopenharmony_ci rc = -EFAULT; 150462306a36Sopenharmony_ci if (copy_from_user(&dtefacs, argp, sizeof(dtefacs))) 150562306a36Sopenharmony_ci break; 150662306a36Sopenharmony_ci rc = -EINVAL; 150762306a36Sopenharmony_ci lock_sock(sk); 150862306a36Sopenharmony_ci if (sk->sk_state != TCP_LISTEN && 150962306a36Sopenharmony_ci sk->sk_state != TCP_CLOSE) 151062306a36Sopenharmony_ci goto out_dtefac_release; 151162306a36Sopenharmony_ci if (dtefacs.calling_len > X25_MAX_AE_LEN) 151262306a36Sopenharmony_ci goto out_dtefac_release; 151362306a36Sopenharmony_ci if (dtefacs.called_len > X25_MAX_AE_LEN) 151462306a36Sopenharmony_ci goto out_dtefac_release; 151562306a36Sopenharmony_ci x25->dte_facilities = dtefacs; 151662306a36Sopenharmony_ci rc = 0; 151762306a36Sopenharmony_ciout_dtefac_release: 151862306a36Sopenharmony_ci release_sock(sk); 151962306a36Sopenharmony_ci break; 152062306a36Sopenharmony_ci } 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_ci case SIOCX25GCALLUSERDATA: { 152362306a36Sopenharmony_ci lock_sock(sk); 152462306a36Sopenharmony_ci rc = copy_to_user(argp, &x25->calluserdata, 152562306a36Sopenharmony_ci sizeof(x25->calluserdata)) 152662306a36Sopenharmony_ci ? -EFAULT : 0; 152762306a36Sopenharmony_ci release_sock(sk); 152862306a36Sopenharmony_ci break; 152962306a36Sopenharmony_ci } 153062306a36Sopenharmony_ci 153162306a36Sopenharmony_ci case SIOCX25SCALLUSERDATA: { 153262306a36Sopenharmony_ci struct x25_calluserdata calluserdata; 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_ci rc = -EFAULT; 153562306a36Sopenharmony_ci if (copy_from_user(&calluserdata, argp, sizeof(calluserdata))) 153662306a36Sopenharmony_ci break; 153762306a36Sopenharmony_ci rc = -EINVAL; 153862306a36Sopenharmony_ci if (calluserdata.cudlength > X25_MAX_CUD_LEN) 153962306a36Sopenharmony_ci break; 154062306a36Sopenharmony_ci lock_sock(sk); 154162306a36Sopenharmony_ci x25->calluserdata = calluserdata; 154262306a36Sopenharmony_ci release_sock(sk); 154362306a36Sopenharmony_ci rc = 0; 154462306a36Sopenharmony_ci break; 154562306a36Sopenharmony_ci } 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_ci case SIOCX25GCAUSEDIAG: { 154862306a36Sopenharmony_ci lock_sock(sk); 154962306a36Sopenharmony_ci rc = copy_to_user(argp, &x25->causediag, sizeof(x25->causediag)) 155062306a36Sopenharmony_ci ? -EFAULT : 0; 155162306a36Sopenharmony_ci release_sock(sk); 155262306a36Sopenharmony_ci break; 155362306a36Sopenharmony_ci } 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ci case SIOCX25SCAUSEDIAG: { 155662306a36Sopenharmony_ci struct x25_causediag causediag; 155762306a36Sopenharmony_ci rc = -EFAULT; 155862306a36Sopenharmony_ci if (copy_from_user(&causediag, argp, sizeof(causediag))) 155962306a36Sopenharmony_ci break; 156062306a36Sopenharmony_ci lock_sock(sk); 156162306a36Sopenharmony_ci x25->causediag = causediag; 156262306a36Sopenharmony_ci release_sock(sk); 156362306a36Sopenharmony_ci rc = 0; 156462306a36Sopenharmony_ci break; 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci } 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci case SIOCX25SCUDMATCHLEN: { 156962306a36Sopenharmony_ci struct x25_subaddr sub_addr; 157062306a36Sopenharmony_ci rc = -EINVAL; 157162306a36Sopenharmony_ci lock_sock(sk); 157262306a36Sopenharmony_ci if(sk->sk_state != TCP_CLOSE) 157362306a36Sopenharmony_ci goto out_cud_release; 157462306a36Sopenharmony_ci rc = -EFAULT; 157562306a36Sopenharmony_ci if (copy_from_user(&sub_addr, argp, 157662306a36Sopenharmony_ci sizeof(sub_addr))) 157762306a36Sopenharmony_ci goto out_cud_release; 157862306a36Sopenharmony_ci rc = -EINVAL; 157962306a36Sopenharmony_ci if (sub_addr.cudmatchlength > X25_MAX_CUD_LEN) 158062306a36Sopenharmony_ci goto out_cud_release; 158162306a36Sopenharmony_ci x25->cudmatchlength = sub_addr.cudmatchlength; 158262306a36Sopenharmony_ci rc = 0; 158362306a36Sopenharmony_ciout_cud_release: 158462306a36Sopenharmony_ci release_sock(sk); 158562306a36Sopenharmony_ci break; 158662306a36Sopenharmony_ci } 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci case SIOCX25CALLACCPTAPPRV: { 158962306a36Sopenharmony_ci rc = -EINVAL; 159062306a36Sopenharmony_ci lock_sock(sk); 159162306a36Sopenharmony_ci if (sk->sk_state == TCP_CLOSE) { 159262306a36Sopenharmony_ci clear_bit(X25_ACCPT_APPRV_FLAG, &x25->flags); 159362306a36Sopenharmony_ci rc = 0; 159462306a36Sopenharmony_ci } 159562306a36Sopenharmony_ci release_sock(sk); 159662306a36Sopenharmony_ci break; 159762306a36Sopenharmony_ci } 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_ci case SIOCX25SENDCALLACCPT: { 160062306a36Sopenharmony_ci rc = -EINVAL; 160162306a36Sopenharmony_ci lock_sock(sk); 160262306a36Sopenharmony_ci if (sk->sk_state != TCP_ESTABLISHED) 160362306a36Sopenharmony_ci goto out_sendcallaccpt_release; 160462306a36Sopenharmony_ci /* must call accptapprv above */ 160562306a36Sopenharmony_ci if (test_bit(X25_ACCPT_APPRV_FLAG, &x25->flags)) 160662306a36Sopenharmony_ci goto out_sendcallaccpt_release; 160762306a36Sopenharmony_ci x25_write_internal(sk, X25_CALL_ACCEPTED); 160862306a36Sopenharmony_ci x25->state = X25_STATE_3; 160962306a36Sopenharmony_ci rc = 0; 161062306a36Sopenharmony_ciout_sendcallaccpt_release: 161162306a36Sopenharmony_ci release_sock(sk); 161262306a36Sopenharmony_ci break; 161362306a36Sopenharmony_ci } 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_ci default: 161662306a36Sopenharmony_ci rc = -ENOIOCTLCMD; 161762306a36Sopenharmony_ci break; 161862306a36Sopenharmony_ci } 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci return rc; 162162306a36Sopenharmony_ci} 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_cistatic const struct net_proto_family x25_family_ops = { 162462306a36Sopenharmony_ci .family = AF_X25, 162562306a36Sopenharmony_ci .create = x25_create, 162662306a36Sopenharmony_ci .owner = THIS_MODULE, 162762306a36Sopenharmony_ci}; 162862306a36Sopenharmony_ci 162962306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 163062306a36Sopenharmony_cistatic int compat_x25_subscr_ioctl(unsigned int cmd, 163162306a36Sopenharmony_ci struct compat_x25_subscrip_struct __user *x25_subscr32) 163262306a36Sopenharmony_ci{ 163362306a36Sopenharmony_ci struct compat_x25_subscrip_struct x25_subscr; 163462306a36Sopenharmony_ci struct x25_neigh *nb; 163562306a36Sopenharmony_ci struct net_device *dev; 163662306a36Sopenharmony_ci int rc = -EINVAL; 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci rc = -EFAULT; 163962306a36Sopenharmony_ci if (copy_from_user(&x25_subscr, x25_subscr32, sizeof(*x25_subscr32))) 164062306a36Sopenharmony_ci goto out; 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_ci rc = -EINVAL; 164362306a36Sopenharmony_ci dev = x25_dev_get(x25_subscr.device); 164462306a36Sopenharmony_ci if (dev == NULL) 164562306a36Sopenharmony_ci goto out; 164662306a36Sopenharmony_ci 164762306a36Sopenharmony_ci nb = x25_get_neigh(dev); 164862306a36Sopenharmony_ci if (nb == NULL) 164962306a36Sopenharmony_ci goto out_dev_put; 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_ci dev_put(dev); 165262306a36Sopenharmony_ci 165362306a36Sopenharmony_ci if (cmd == SIOCX25GSUBSCRIP) { 165462306a36Sopenharmony_ci read_lock_bh(&x25_neigh_list_lock); 165562306a36Sopenharmony_ci x25_subscr.extended = nb->extended; 165662306a36Sopenharmony_ci x25_subscr.global_facil_mask = nb->global_facil_mask; 165762306a36Sopenharmony_ci read_unlock_bh(&x25_neigh_list_lock); 165862306a36Sopenharmony_ci rc = copy_to_user(x25_subscr32, &x25_subscr, 165962306a36Sopenharmony_ci sizeof(*x25_subscr32)) ? -EFAULT : 0; 166062306a36Sopenharmony_ci } else { 166162306a36Sopenharmony_ci rc = -EINVAL; 166262306a36Sopenharmony_ci if (x25_subscr.extended == 0 || x25_subscr.extended == 1) { 166362306a36Sopenharmony_ci rc = 0; 166462306a36Sopenharmony_ci write_lock_bh(&x25_neigh_list_lock); 166562306a36Sopenharmony_ci nb->extended = x25_subscr.extended; 166662306a36Sopenharmony_ci nb->global_facil_mask = x25_subscr.global_facil_mask; 166762306a36Sopenharmony_ci write_unlock_bh(&x25_neigh_list_lock); 166862306a36Sopenharmony_ci } 166962306a36Sopenharmony_ci } 167062306a36Sopenharmony_ci x25_neigh_put(nb); 167162306a36Sopenharmony_ciout: 167262306a36Sopenharmony_ci return rc; 167362306a36Sopenharmony_ciout_dev_put: 167462306a36Sopenharmony_ci dev_put(dev); 167562306a36Sopenharmony_ci goto out; 167662306a36Sopenharmony_ci} 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_cistatic int compat_x25_ioctl(struct socket *sock, unsigned int cmd, 167962306a36Sopenharmony_ci unsigned long arg) 168062306a36Sopenharmony_ci{ 168162306a36Sopenharmony_ci void __user *argp = compat_ptr(arg); 168262306a36Sopenharmony_ci int rc = -ENOIOCTLCMD; 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_ci switch(cmd) { 168562306a36Sopenharmony_ci case TIOCOUTQ: 168662306a36Sopenharmony_ci case TIOCINQ: 168762306a36Sopenharmony_ci rc = x25_ioctl(sock, cmd, (unsigned long)argp); 168862306a36Sopenharmony_ci break; 168962306a36Sopenharmony_ci case SIOCGIFADDR: 169062306a36Sopenharmony_ci case SIOCSIFADDR: 169162306a36Sopenharmony_ci case SIOCGIFDSTADDR: 169262306a36Sopenharmony_ci case SIOCSIFDSTADDR: 169362306a36Sopenharmony_ci case SIOCGIFBRDADDR: 169462306a36Sopenharmony_ci case SIOCSIFBRDADDR: 169562306a36Sopenharmony_ci case SIOCGIFNETMASK: 169662306a36Sopenharmony_ci case SIOCSIFNETMASK: 169762306a36Sopenharmony_ci case SIOCGIFMETRIC: 169862306a36Sopenharmony_ci case SIOCSIFMETRIC: 169962306a36Sopenharmony_ci rc = -EINVAL; 170062306a36Sopenharmony_ci break; 170162306a36Sopenharmony_ci case SIOCADDRT: 170262306a36Sopenharmony_ci case SIOCDELRT: 170362306a36Sopenharmony_ci rc = -EPERM; 170462306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 170562306a36Sopenharmony_ci break; 170662306a36Sopenharmony_ci rc = x25_route_ioctl(cmd, argp); 170762306a36Sopenharmony_ci break; 170862306a36Sopenharmony_ci case SIOCX25GSUBSCRIP: 170962306a36Sopenharmony_ci rc = compat_x25_subscr_ioctl(cmd, argp); 171062306a36Sopenharmony_ci break; 171162306a36Sopenharmony_ci case SIOCX25SSUBSCRIP: 171262306a36Sopenharmony_ci rc = -EPERM; 171362306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 171462306a36Sopenharmony_ci break; 171562306a36Sopenharmony_ci rc = compat_x25_subscr_ioctl(cmd, argp); 171662306a36Sopenharmony_ci break; 171762306a36Sopenharmony_ci case SIOCX25GFACILITIES: 171862306a36Sopenharmony_ci case SIOCX25SFACILITIES: 171962306a36Sopenharmony_ci case SIOCX25GDTEFACILITIES: 172062306a36Sopenharmony_ci case SIOCX25SDTEFACILITIES: 172162306a36Sopenharmony_ci case SIOCX25GCALLUSERDATA: 172262306a36Sopenharmony_ci case SIOCX25SCALLUSERDATA: 172362306a36Sopenharmony_ci case SIOCX25GCAUSEDIAG: 172462306a36Sopenharmony_ci case SIOCX25SCAUSEDIAG: 172562306a36Sopenharmony_ci case SIOCX25SCUDMATCHLEN: 172662306a36Sopenharmony_ci case SIOCX25CALLACCPTAPPRV: 172762306a36Sopenharmony_ci case SIOCX25SENDCALLACCPT: 172862306a36Sopenharmony_ci rc = x25_ioctl(sock, cmd, (unsigned long)argp); 172962306a36Sopenharmony_ci break; 173062306a36Sopenharmony_ci default: 173162306a36Sopenharmony_ci rc = -ENOIOCTLCMD; 173262306a36Sopenharmony_ci break; 173362306a36Sopenharmony_ci } 173462306a36Sopenharmony_ci return rc; 173562306a36Sopenharmony_ci} 173662306a36Sopenharmony_ci#endif 173762306a36Sopenharmony_ci 173862306a36Sopenharmony_cistatic const struct proto_ops x25_proto_ops = { 173962306a36Sopenharmony_ci .family = AF_X25, 174062306a36Sopenharmony_ci .owner = THIS_MODULE, 174162306a36Sopenharmony_ci .release = x25_release, 174262306a36Sopenharmony_ci .bind = x25_bind, 174362306a36Sopenharmony_ci .connect = x25_connect, 174462306a36Sopenharmony_ci .socketpair = sock_no_socketpair, 174562306a36Sopenharmony_ci .accept = x25_accept, 174662306a36Sopenharmony_ci .getname = x25_getname, 174762306a36Sopenharmony_ci .poll = datagram_poll, 174862306a36Sopenharmony_ci .ioctl = x25_ioctl, 174962306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 175062306a36Sopenharmony_ci .compat_ioctl = compat_x25_ioctl, 175162306a36Sopenharmony_ci#endif 175262306a36Sopenharmony_ci .gettstamp = sock_gettstamp, 175362306a36Sopenharmony_ci .listen = x25_listen, 175462306a36Sopenharmony_ci .shutdown = sock_no_shutdown, 175562306a36Sopenharmony_ci .setsockopt = x25_setsockopt, 175662306a36Sopenharmony_ci .getsockopt = x25_getsockopt, 175762306a36Sopenharmony_ci .sendmsg = x25_sendmsg, 175862306a36Sopenharmony_ci .recvmsg = x25_recvmsg, 175962306a36Sopenharmony_ci .mmap = sock_no_mmap, 176062306a36Sopenharmony_ci}; 176162306a36Sopenharmony_ci 176262306a36Sopenharmony_cistatic struct packet_type x25_packet_type __read_mostly = { 176362306a36Sopenharmony_ci .type = cpu_to_be16(ETH_P_X25), 176462306a36Sopenharmony_ci .func = x25_lapb_receive_frame, 176562306a36Sopenharmony_ci}; 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_cistatic struct notifier_block x25_dev_notifier = { 176862306a36Sopenharmony_ci .notifier_call = x25_device_event, 176962306a36Sopenharmony_ci}; 177062306a36Sopenharmony_ci 177162306a36Sopenharmony_civoid x25_kill_by_neigh(struct x25_neigh *nb) 177262306a36Sopenharmony_ci{ 177362306a36Sopenharmony_ci struct sock *s; 177462306a36Sopenharmony_ci 177562306a36Sopenharmony_ci write_lock_bh(&x25_list_lock); 177662306a36Sopenharmony_ci 177762306a36Sopenharmony_ci sk_for_each(s, &x25_list) { 177862306a36Sopenharmony_ci if (x25_sk(s)->neighbour == nb) { 177962306a36Sopenharmony_ci write_unlock_bh(&x25_list_lock); 178062306a36Sopenharmony_ci lock_sock(s); 178162306a36Sopenharmony_ci x25_disconnect(s, ENETUNREACH, 0, 0); 178262306a36Sopenharmony_ci release_sock(s); 178362306a36Sopenharmony_ci write_lock_bh(&x25_list_lock); 178462306a36Sopenharmony_ci } 178562306a36Sopenharmony_ci } 178662306a36Sopenharmony_ci write_unlock_bh(&x25_list_lock); 178762306a36Sopenharmony_ci 178862306a36Sopenharmony_ci /* Remove any related forwards */ 178962306a36Sopenharmony_ci x25_clear_forward_by_dev(nb->dev); 179062306a36Sopenharmony_ci} 179162306a36Sopenharmony_ci 179262306a36Sopenharmony_cistatic int __init x25_init(void) 179362306a36Sopenharmony_ci{ 179462306a36Sopenharmony_ci int rc; 179562306a36Sopenharmony_ci 179662306a36Sopenharmony_ci rc = proto_register(&x25_proto, 0); 179762306a36Sopenharmony_ci if (rc) 179862306a36Sopenharmony_ci goto out; 179962306a36Sopenharmony_ci 180062306a36Sopenharmony_ci rc = sock_register(&x25_family_ops); 180162306a36Sopenharmony_ci if (rc) 180262306a36Sopenharmony_ci goto out_proto; 180362306a36Sopenharmony_ci 180462306a36Sopenharmony_ci dev_add_pack(&x25_packet_type); 180562306a36Sopenharmony_ci 180662306a36Sopenharmony_ci rc = register_netdevice_notifier(&x25_dev_notifier); 180762306a36Sopenharmony_ci if (rc) 180862306a36Sopenharmony_ci goto out_sock; 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_ci rc = x25_register_sysctl(); 181162306a36Sopenharmony_ci if (rc) 181262306a36Sopenharmony_ci goto out_dev; 181362306a36Sopenharmony_ci 181462306a36Sopenharmony_ci rc = x25_proc_init(); 181562306a36Sopenharmony_ci if (rc) 181662306a36Sopenharmony_ci goto out_sysctl; 181762306a36Sopenharmony_ci 181862306a36Sopenharmony_ci pr_info("Linux Version 0.2\n"); 181962306a36Sopenharmony_ci 182062306a36Sopenharmony_ciout: 182162306a36Sopenharmony_ci return rc; 182262306a36Sopenharmony_ciout_sysctl: 182362306a36Sopenharmony_ci x25_unregister_sysctl(); 182462306a36Sopenharmony_ciout_dev: 182562306a36Sopenharmony_ci unregister_netdevice_notifier(&x25_dev_notifier); 182662306a36Sopenharmony_ciout_sock: 182762306a36Sopenharmony_ci dev_remove_pack(&x25_packet_type); 182862306a36Sopenharmony_ci sock_unregister(AF_X25); 182962306a36Sopenharmony_ciout_proto: 183062306a36Sopenharmony_ci proto_unregister(&x25_proto); 183162306a36Sopenharmony_ci goto out; 183262306a36Sopenharmony_ci} 183362306a36Sopenharmony_cimodule_init(x25_init); 183462306a36Sopenharmony_ci 183562306a36Sopenharmony_cistatic void __exit x25_exit(void) 183662306a36Sopenharmony_ci{ 183762306a36Sopenharmony_ci x25_proc_exit(); 183862306a36Sopenharmony_ci x25_link_free(); 183962306a36Sopenharmony_ci x25_route_free(); 184062306a36Sopenharmony_ci 184162306a36Sopenharmony_ci x25_unregister_sysctl(); 184262306a36Sopenharmony_ci 184362306a36Sopenharmony_ci unregister_netdevice_notifier(&x25_dev_notifier); 184462306a36Sopenharmony_ci 184562306a36Sopenharmony_ci dev_remove_pack(&x25_packet_type); 184662306a36Sopenharmony_ci 184762306a36Sopenharmony_ci sock_unregister(AF_X25); 184862306a36Sopenharmony_ci proto_unregister(&x25_proto); 184962306a36Sopenharmony_ci} 185062306a36Sopenharmony_cimodule_exit(x25_exit); 185162306a36Sopenharmony_ci 185262306a36Sopenharmony_ciMODULE_AUTHOR("Jonathan Naylor <g4klx@g4klx.demon.co.uk>"); 185362306a36Sopenharmony_ciMODULE_DESCRIPTION("The X.25 Packet Layer network layer protocol"); 185462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 185562306a36Sopenharmony_ciMODULE_ALIAS_NETPROTO(PF_X25); 1856