cregit-Linux how code gets into the kernel

Release 4.14 net/x25/af_x25.c

Directory: net/x25
/*
 *      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
 *      2002-10-06      Arnaldo C. Melo Get rid of cli/sti, move proc stuff to
 *                                      x25_proc.c, using seq_file
 *      2005-04-02      Shaun Pereira   Selective sub address matching
 *                                      with call user data
 *      2005-04-15      Shaun Pereira   Fast select with no restriction on
 *                                      response
 */


#define pr_fmt(fmt) "X25: " fmt

#include <linux/module.h>
#include <linux/capability.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/sched/signal.h>
#include <linux/timer.h>
#include <linux/string.h>
#include <linux/net.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <net/sock.h>
#include <net/tcp_states.h>
#include <linux/uaccess.h>
#include <linux/fcntl.h>
#include <linux/termios.h>	/* For TIOCINQ/OUTQ */
#include <linux/notifier.h>
#include <linux/init.h>
#include <linux/compat.h>
#include <linux/ctype.h>

#include <net/x25.h>
#include <net/compat.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;

int sysctl_x25_forward                 = 0;


HLIST_HEAD(x25_list);

DEFINE_RWLOCK(x25_list_lock);


static const struct proto_ops x25_proto_ops;


static const struct x25_address null_x25_address = {"               "};

#ifdef CONFIG_COMPAT

struct compat_x25_subscrip_struct {
	
char device[200-sizeof(compat_ulong_t)];
	
compat_ulong_t global_facil_mask;
	
compat_uint_t extended;
};
#endif



int x25_parse_address_block(struct sk_buff *skb, struct x25_address *called_addr, struct x25_address *calling_addr) { unsigned char len; int needed; int rc; if (!pskb_may_pull(skb, 1)) { /* packet has no address block */ rc = 0; goto empty; } len = *skb->data; needed = 1 + (len >> 4) + (len & 0x0f); if (!pskb_may_pull(skb, needed)) { /* packet is too short to hold the addresses it claims to hold */ rc = -1; goto empty; } return x25_addr_ntoa(skb->data, called_addr, calling_addr); empty: *called_addr->x25_addr = 0; *calling_addr->x25_addr = 0; return rc; }

Contributors

PersonTokensPropCommitsCommitProp
John Hughes11592.00%150.00%
Matthew Daley108.00%150.00%
Total125100.00%2100.00%


int x25_addr_ntoa(unsigned char *p, struct x25_address *called_addr, struct x25_address *calling_addr) { unsigned int called_len, calling_len; char *called, *calling; unsigned 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 = *calling = '\0'; return 1 + (called_len + calling_len + 1) / 2; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)22198.22%133.33%
Eric Dumazet20.89%133.33%
Arnaldo Carvalho de Melo20.89%133.33%
Total225100.00%3100.00%


int x25_addr_aton(unsigned char *p, struct x25_address *called_addr, struct 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; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)21399.07%266.67%
Arnaldo Carvalho de Melo20.93%133.33%
Total215100.00%3100.00%

/* * Socket removal during an interrupt is now safe. */
static void x25_remove_socket(struct sock *sk) { write_lock_bh(&x25_list_lock); sk_del_node_init(sk); write_unlock_bh(&x25_list_lock); }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)1864.29%250.00%
Arnaldo Carvalho de Melo1035.71%250.00%
Total28100.00%4100.00%

/* * Kill all bound sockets on a dropped device. */
static void x25_kill_by_device(struct net_device *dev) { struct sock *s; write_lock_bh(&x25_list_lock); sk_for_each(s, &x25_list) if (x25_sk(s)->neighbour && x25_sk(s)->neighbour->dev == dev) x25_disconnect(s, ENETUNREACH, 0, 0); write_unlock_bh(&x25_list_lock); }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)4163.08%457.14%
Arnaldo Carvalho de Melo1827.69%228.57%
David S. Miller69.23%114.29%
Total65100.00%7100.00%

/* * Handle device status changes. */
static int x25_device_event(struct notifier_block *this, unsigned long event, void *ptr) { struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct x25_neigh *nb; if (!net_eq(dev_net(dev), &init_net)) return NOTIFY_DONE; if (dev->type == ARPHRD_X25 #if IS_ENABLED(CONFIG_LLC) || dev->type == ARPHRD_ETHER #endif ) { switch (event) { case NETDEV_UP: x25_link_device_up(dev); break; case NETDEV_GOING_DOWN: nb = x25_get_neigh(dev); if (nb) { x25_terminate_link(nb); x25_neigh_put(nb); } break; case NETDEV_DOWN: x25_kill_by_device(dev); x25_route_device_down(dev); x25_link_device_down(dev); break; } } return NOTIFY_DONE; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)10374.10%440.00%
Arnaldo Carvalho de Melo1410.07%110.00%
Eric W. Biedermann107.19%110.00%
Hideaki Yoshifuji / 吉藤英明85.76%220.00%
Jiri Pirko32.16%110.00%
Igor Maravić10.72%110.00%
Total139100.00%10100.00%

/* * Add a socket to the bound sockets list. */
static void x25_insert_socket(struct sock *sk) { write_lock_bh(&x25_list_lock); sk_add_node(sk, &x25_list); write_unlock_bh(&x25_list_lock); }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)1961.29%250.00%
Arnaldo Carvalho de Melo1238.71%250.00%
Total31100.00%4100.00%

/* * Find a socket that wants to accept the Call Request we just * received. Check the full list for an address/cud match. * If no cuds match return the next_best thing, an address match. * Note: if a listening socket has cud set it must only get calls * with matching cud. */
static struct sock *x25_find_listener(struct x25_address *addr, struct sk_buff *skb) { struct sock *s; struct sock *next_best; read_lock_bh(&x25_list_lock); next_best = NULL; sk_for_each(s, &x25_list) if ((!strcmp(addr->x25_addr, x25_sk(s)->source_addr.x25_addr) || !strcmp(addr->x25_addr, null_x25_address.x25_addr)) && s->sk_state == TCP_LISTEN) { /* * Found a listening socket, now check the incoming * call user data vs this sockets call user data */ if (x25_sk(s)->cudmatchlength > 0 && skb->len >= x25_sk(s)->cudmatchlength) { if((memcmp(x25_sk(s)->calluserdata.cuddata, skb->data, x25_sk(s)->cudmatchlength)) == 0) { sock_hold(s); goto found; } } else next_best = s; } if (next_best) { s = next_best; sock_hold(s); goto found; } s = NULL; found: read_unlock_bh(&x25_list_lock); return s; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)5931.55%218.18%
Andrew Hendry4825.67%19.09%
Shaun Pereira3518.72%19.09%
Arnaldo Carvalho de Melo3217.11%545.45%
Matthew Daley94.81%19.09%
David S. Miller42.14%19.09%
Total187100.00%11100.00%

/* * Find a connected X.25 socket given my LCI and neighbour. */
static struct sock *__x25_find_socket(unsigned int lci, struct x25_neigh *nb) { struct sock *s; sk_for_each(s, &x25_list) if (x25_sk(s)->lci == lci && x25_sk(s)->neighbour == nb) { sock_hold(s); goto found; } s = NULL; found: return s; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)3247.76%228.57%
Arnaldo Carvalho de Melo2841.79%342.86%
David S. Miller68.96%114.29%
Adrian Bunk11.49%114.29%
Total67100.00%7100.00%


struct sock *x25_find_socket(unsigned int lci, struct x25_neigh *nb) { struct sock *s; read_lock_bh(&x25_list_lock); s = __x25_find_socket(lci, nb); read_unlock_bh(&x25_list_lock); return s; }

Contributors

PersonTokensPropCommitsCommitProp
Arnaldo Carvalho de Melo3986.67%150.00%
Linus Torvalds (pre-git)613.33%150.00%
Total45100.00%2100.00%

/* * Find a unique LCI for a given device. */
static unsigned int x25_new_lci(struct x25_neigh *nb) { unsigned int lci = 1; struct sock *sk; read_lock_bh(&x25_list_lock); while ((sk = __x25_find_socket(lci, nb)) != NULL) { sock_put(sk); if (++lci == 4096) { lci = 0; break; } } read_unlock_bh(&x25_list_lock); return lci; }

Contributors

PersonTokensPropCommitsCommitProp
Arnaldo Carvalho de Melo3952.70%350.00%
Linus Torvalds (pre-git)3445.95%233.33%
Adrian Bunk11.35%116.67%
Total74100.00%6100.00%

/* * Deferred destroy. */ static void __x25_destroy_socket(struct sock *); /* * handler for deferred kills. */
static void x25_destroy_timer(unsigned long data) { x25_destroy_socket_from_timer((struct sock *)data); }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)1995.00%150.00%
David S. Miller15.00%150.00%
Total20100.00%2100.00%

/* * 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. * Not static as it's used by the timer */
static void __x25_destroy_socket(struct sock *sk) { struct sk_buff *skb; x25_stop_heartbeat(sk); x25_stop_timer(sk); x25_remove_socket(sk); x25_clear_queues(sk); /* Flush the queues */ while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) { if (skb->sk != sk) { /* A pending connection */ /* * Queue the unaccepted socket for death */ skb->sk->sk_state = TCP_LISTEN; sock_set_flag(skb->sk, SOCK_DEAD); x25_start_heartbeat(skb->sk); x25_sk(skb->sk)->state = X25_STATE_0; } kfree_skb(skb); } if (sk_has_allocations(sk)) { /* Defer: outstanding buffers */ sk->sk_timer.expires = jiffies + 10 * HZ; sk->sk_timer.function = x25_destroy_timer; sk->sk_timer.data = (unsigned long)sk; add_timer(&sk->sk_timer); } else { /* drop last reference so sock_put will free */ __sock_put(sk); } }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)12575.30%323.08%
Andrew Morton127.23%17.69%
Andrew Hendry84.82%17.69%
Arnaldo Carvalho de Melo84.82%323.08%
David S. Miller63.61%215.38%
Stephen Hemminger42.41%17.69%
James Morris21.20%17.69%
Eric Dumazet10.60%17.69%
Total166100.00%13100.00%


void x25_destroy_socket_from_timer(struct sock *sk) { sock_hold(sk); bh_lock_sock(sk); __x25_destroy_socket(sk); bh_unlock_sock(sk); sock_put(sk); }

Contributors

PersonTokensPropCommitsCommitProp
David S. Miller35100.00%1100.00%
Total35100.00%1100.00%

/* * 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 __user *optval, unsigned int optlen) { int opt; struct sock *sk = sock->sk; int rc = -ENOPROTOOPT; if (level != SOL_X25 || optname != X25_QBITINCL) goto out; rc = -EINVAL; if (optlen < sizeof(int)) goto out; rc = -EFAULT; if (get_user(opt, (int __user *)optval)) goto out; if (opt) set_bit(X25_Q_BIT_FLAG, &x25_sk(sk)->flags); else clear_bit(X25_Q_BIT_FLAG, &x25_sk(sk)->flags); rc = 0; out: return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)7152.21%337.50%
Arnaldo Carvalho de Melo3425.00%112.50%
Andrew Hendry2518.38%112.50%
David S. Miller42.94%225.00%
Al Viro21.47%112.50%
Total136100.00%8100.00%


static int x25_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) { struct sock *sk = sock->sk; int val, len, rc = -ENOPROTOOPT; if (level != SOL_X25 || optname != X25_QBITINCL) goto out; rc = -EFAULT; if (get_user(len, optlen)) goto out; len = min_t(unsigned int, len, sizeof(int)); rc = -EINVAL; if (len < 0) goto out; rc = -EFAULT; if (put_user(len, optlen)) goto out; val = test_bit(X25_Q_BIT_FLAG, &x25_sk(sk)->flags); rc = copy_to_user(optval, &val, len) ? -EFAULT : 0; out: return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)8454.19%440.00%
Arnaldo Carvalho de Melo5032.26%110.00%
Linus Torvalds127.74%330.00%
Andrew Hendry74.52%110.00%
Al Viro21.29%110.00%
Total155100.00%10100.00%


static int x25_listen(struct socket *sock, int backlog) { struct sock *sk = sock->sk; int rc = -EOPNOTSUPP; lock_sock(sk); if (sk->sk_state != TCP_LISTEN) { memset(&x25_sk(sk)->dest_addr, 0, X25_ADDR_LEN); sk->sk_max_ack_backlog = backlog; sk->sk_state = TCP_LISTEN; rc = 0; } release_sock(sk); return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)5667.47%228.57%
Arnaldo Carvalho de Melo1315.66%228.57%
Andrew Hendry89.64%114.29%
David S. Miller44.82%114.29%
Arnd Bergmann22.41%114.29%
Total83100.00%7100.00%

static struct proto x25_proto = { .name = "X25", .owner = THIS_MODULE, .obj_size = sizeof(struct x25_sock), };
static struct sock *x25_alloc_socket(struct net *net, int kern) { struct x25_sock *x25; struct sock *sk = sk_alloc(net, AF_X25, GFP_ATOMIC, &x25_proto, kern); if (!sk) goto out; sock_init_data(NULL, sk); x25 = x25_sk(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); out: return sk; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)6464.65%650.00%
Arnaldo Carvalho de Melo2020.20%325.00%
Eric W. Biedermann1111.11%216.67%
David S. Miller44.04%18.33%
Total99100.00%12100.00%


static int x25_create(struct net *net, struct socket *sock, int protocol, int kern) { struct sock *sk; struct x25_sock *x25; int rc = -EAFNOSUPPORT; if (!net_eq(net, &init_net)) goto out; rc = -ESOCKTNOSUPPORT; if (sock->type != SOCK_SEQPACKET) goto out; rc = -EINVAL; if (protocol) goto out; rc = -ENOBUFS; if ((sk = x25_alloc_socket(net, kern)) == NULL) goto out; x25 = x25_sk(sk); sock_init_data(sock, sk); x25_init_timers(sk); sock->ops = &x25_proto_ops; sk->sk_protocol = protocol; sk->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->cudmatchlength = 0; set_bit(X25_ACCPT_APPRV_FLAG, &x25->flags); /* normally no cud */ /* on call accept */ 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 = 0; /* by default don't negotiate throughput */ x25->facilities.reverse = X25_DEFAULT_REVERSE; x25->dte_facilities.calling_len = 0; x25->dte_facilities.called_len = 0; memset(x25->dte_facilities.called_ae, '\0', sizeof(x25->dte_facilities.called_ae)); memset(x25->dte_facilities.calling_ae, '\0', sizeof(x25->dte_facilities.calling_ae)); rc = 0; out: return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)15250.17%627.27%
Shaun Pereira6722.11%313.64%
Andrew Hendry278.91%29.09%
Arnaldo Carvalho de Melo258.25%418.18%
Eric W. Biedermann175.61%29.09%
Octavian Purdila51.65%14.55%
Eric Paris30.99%14.55%
David S. Miller30.99%14.55%
John Hughes20.66%14.55%
Vinay K. Nallamothu20.66%14.55%
Total303100.00%22100.00%


static struct sock *x25_make_new(struct sock *osk) { struct sock *sk = NULL; struct x25_sock *x25, *ox25; if (osk->sk_type != SOCK_SEQPACKET) goto out; if ((sk = x25_alloc_socket(sock_net(osk), 0)) == NULL) goto out; x25 = x25_sk(sk); sk->sk_type = osk->sk_type; sk->sk_priority = osk->sk_priority; sk->sk_protocol = osk->sk_protocol; sk->sk_rcvbuf = osk->sk_rcvbuf; sk->sk_sndbuf = osk->sk_sndbuf; sk->sk_state = TCP_ESTABLISHED; sk->sk_backlog_rcv = osk->sk_backlog_rcv; sock_copy_flags(sk, osk); ox25 = x25_sk(osk); x25->t21 = ox25->t21; x25->t22 = ox25->t22; x25->t23 = ox25->t23; x25->t2 = ox25->t2; x25->flags = ox25->flags; x25->facilities = ox25->facilities; x25->dte_facilities = ox25->dte_facilities; x25->cudmatchlength = ox25->cudmatchlength; clear_bit(X25_INTERRUPT_FLAG, &x25->flags); x25_init_timers(sk); out: return sk; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)12757.99%315.79%
Arnaldo Carvalho de Melo2611.87%421.05%
David S. Miller198.68%15.26%
Shaun Pereira177.76%315.79%
Andrew Hendry146.39%210.53%
Thomas Graf62.74%210.53%
Eric W. Biedermann52.28%210.53%
Hideaki Yoshifuji / 吉藤英明31.37%15.26%
Vinay K. Nallamothu20.91%15.26%
Total219100.00%19100.00%


static int x25_release(struct socket *sock) { struct sock *sk = sock->sk; struct x25_sock *x25; if (!sk) return 0; x25 = x25_sk(sk); sock_hold(sk); lock_sock(sk); switch (x25->state) { case X25_STATE_0: case X25_STATE_2: x25_disconnect(sk, 0, 0, 0); __x25_destroy_socket(sk); goto out; 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); x25->state = X25_STATE_2; sk->sk_state = TCP_CLOSE; sk->sk_shutdown |= SEND_SHUTDOWN; sk->sk_state_change(sk); sock_set_flag(sk, SOCK_DEAD); sock_set_flag(sk, SOCK_DESTROY); break; } sock_orphan(sk); out: release_sock(sk); sock_put(sk); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)11165.68%321.43%
Arnd Bergmann2414.20%214.29%
Arnaldo Carvalho de Melo148.28%535.71%
David S. Miller137.69%214.29%
James Morris42.37%17.14%
Stephen Hemminger31.78%17.14%
Total169100.00%14100.00%


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; int len, i, rc = 0; if (!sock_flag(sk, SOCK_ZAPPED) || addr_len != sizeof(struct sockaddr_x25) || addr->sx25_family != AF_X25) { rc = -EINVAL; goto out; } len = strlen(addr->sx25_addr.x25_addr); for (i = 0; i < len; i++) { if (!isdigit(addr->sx25_addr.x25_addr[i])) { rc = -EINVAL; goto out; } } lock_sock(sk); x25_sk(sk)->source_addr = addr->sx25_addr; x25_insert_socket(sk); sock_reset_flag(sk, SOCK_ZAPPED); release_sock(sk); SOCK_DEBUG(sk, "x25_bind: socket is bound\n"); out: return rc;