// SPDX-License-Identifier: GPL-2.0-or-later /* * 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 * * 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
*/
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;
for (i = 0; i < (called_len + calling_len); i++) { if (i < called_len) { if (i % 2 != 0) {
*p |= (*called++ - '0') << 0;
p++;
} else {
*p = 0x00;
*p |= (*called++ - '0') << 4;
}
} else { if (i % 2 != 0) {
*p |= (*calling++ - '0') << 0;
p++;
} else {
*p = 0x00;
*p |= (*calling++ - '0') << 4;
}
}
}
return 1 + (called_len + calling_len + 1) / 2;
}
/* * Socket removal during an interrupt is now safe.
*/ staticvoid x25_remove_socket(struct sock *sk)
{
write_lock_bh(&x25_list_lock);
sk_del_node_init(sk);
write_unlock_bh(&x25_list_lock);
}
if (!net_eq(dev_net(dev), &init_net)) return NOTIFY_DONE;
if (dev->type == ARPHRD_X25) { switch (event) { case NETDEV_REGISTER: case NETDEV_POST_TYPE_CHANGE:
x25_link_device_up(dev); break; case NETDEV_DOWN:
nb = x25_get_neigh(dev); if (nb) {
x25_link_terminated(nb);
x25_neigh_put(nb);
}
x25_route_device_down(dev); break; case NETDEV_PRE_TYPE_CHANGE: case NETDEV_UNREGISTER:
x25_link_device_down(dev); break; case NETDEV_CHANGE: if (!netif_carrier_ok(dev)) {
nb = x25_get_neigh(dev); if (nb) {
x25_link_terminated(nb);
x25_neigh_put(nb);
}
} break;
}
}
return NOTIFY_DONE;
}
/* * Add a socket to the bound sockets list.
*/ staticvoid x25_insert_socket(struct sock *sk)
{
write_lock_bh(&x25_list_lock);
sk_add_node(sk, &x25_list);
write_unlock_bh(&x25_list_lock);
}
/* * 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.
*/ staticstruct 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(x25_sk(s)->source_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;
}
/* * Find a connected X.25 socket given my LCI and neighbour.
*/ staticstruct sock *__x25_find_socket(unsignedint lci, struct x25_neigh *nb)
{ struct sock *s;
/* * This is called from user mode and the timers. Thus it protects itself * against interrupting 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
*/ staticvoid __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;
add_timer(&sk->sk_timer);
} else { /* drop last reference so sock_put will free */
__sock_put(sk);
}
}
/* * Remove the LCI and frame type.
*/
skb_pull(skb, X25_STD_MIN_LEN);
/* * Extract the X.25 addresses and convert them to ASCII strings, * and remove them. * * Address block is mandatory in call request packets
*/
addr_len = x25_parse_address_block(skb, &source_addr, &dest_addr); if (addr_len <= 0) goto out_clear_request;
skb_pull(skb, addr_len);
/* * Get the length of the facilities, skip past them for the moment * get the call user data because this is needed to determine * the correct listener * * Facilities length is mandatory in call request packets
*/ if (!pskb_may_pull(skb, 1)) goto out_clear_request;
len = skb->data[0] + 1; if (!pskb_may_pull(skb, len)) goto out_clear_request;
skb_pull(skb,len);
/* * Ensure that the amount of call user data is valid.
*/ if (skb->len > X25_MAX_CUD_LEN) goto out_clear_request;
/* * Get all the call user data so it can be used in * x25_find_listener and skb_copy_from_linear_data up ahead.
*/ if (!pskb_may_pull(skb, skb->len)) goto out_clear_request;
/* * Find a listener for the particular address/cud pair.
*/
sk = x25_find_listener(&source_addr,skb);
skb_push(skb,len);
if (sk != NULL && sk_acceptq_is_full(sk)) { goto out_sock_put;
}
/* * We dont have any listeners for this incoming call. * Try forwarding it.
*/ if (sk == NULL) {
skb_push(skb, addr_len + X25_STD_MIN_LEN); if (sysctl_x25_forward &&
x25_forward_call(&dest_addr, nb, skb, lci) > 0)
{ /* Call was forwarded, dont process it any more */
kfree_skb(skb);
rc = 1; goto out;
} else { /* No listeners, can't forward, clear the call */ goto out_clear_request;
}
}
/* * Try to reach a compromise on the requested facilities.
*/
len = x25_negotiate_facilities(skb, sk, &facilities, &dte_facilities); if (len == -1) goto out_sock_put;
/* * current neighbour/link might impose additional limits * on certain facilities
*/
x25_limit_facilities(&facilities, nb);
/* * Try to create a new socket.
*/
make = x25_make_new(sk); if (!make) goto out_sock_put;
if (usx25) {
rc = -EINVAL; if (msg->msg_namelen < sizeof(sx25)) goto out;
memcpy(&sx25, usx25, sizeof(sx25));
rc = -EISCONN; if (strcmp(x25->dest_addr.x25_addr, sx25.sx25_addr.x25_addr)) goto out;
rc = -EINVAL; if (sx25.sx25_family != AF_X25) goto out;
} else { /* * FIXME 1003.1g - if the socket is like this because * it has become closed (not started closed) we ought * to SIGPIPE, EPIPE;
*/
rc = -ENOTCONN; if (sk->sk_state != TCP_ESTABLISHED) goto out;
rc = memcpy_from_msg(skb_transport_header(skb), msg, len); if (rc) goto out_kfree_skb;
/* * If the Q BIT Include socket option is in force, the first * byte of the user data is the logical value of the Q Bit.
*/ if (test_bit(X25_Q_BIT_FLAG, &x25->flags)) { if (!pskb_may_pull(skb, 1)) goto out_kfree_skb;
qbit = skb->data[0];
skb_pull(skb, 1);
}
/* * Push down the X.25 header
*/
net_dbg_ratelimited("x25_sendmsg: Building X.25 Header.\n");
/* * This works for seqpacket too. The receiver has ordered the queue for * us! We do one quick check first though
*/ if (sk->sk_state != TCP_ESTABLISHED) goto out;
if (flags & MSG_OOB) {
rc = -EINVAL; if (sock_flag(sk, SOCK_URGINLINE) ||
!skb_peek(&x25->interrupt_in_queue)) goto out;
skb = skb_dequeue(&x25->interrupt_in_queue);
if (!pskb_may_pull(skb, X25_STD_MIN_LEN)) goto out_free_dgram;
skb_pull(skb, X25_STD_MIN_LEN);
/* * No Q bit information on Interrupt data.
*/ if (test_bit(X25_Q_BIT_FLAG, &x25->flags)) {
asmptr = skb_push(skb, 1);
*asmptr = 0x00;
}
msg->msg_flags |= MSG_OOB;
} else { /* Now we can treat all alike */
release_sock(sk);
skb = skb_recv_datagram(sk, flags, &rc);
lock_sock(sk); if (!skb) goto out;
if (!pskb_may_pull(skb, header_len)) goto out_free_dgram;
case TIOCINQ: { struct sk_buff *skb; int amount = 0; /* * These two are safe on a single CPU system as * only user tasks fiddle here
*/
lock_sock(sk); if ((skb = skb_peek(&sk->sk_receive_queue)) != NULL)
amount = skb->len;
release_sock(sk);
rc = put_user(amount, (unsignedint __user *)argp); break;
}
case SIOCGIFADDR: case SIOCSIFADDR: case SIOCGIFDSTADDR: case SIOCSIFDSTADDR: case SIOCGIFBRDADDR: case SIOCSIFBRDADDR: case SIOCGIFNETMASK: case SIOCSIFNETMASK: case SIOCGIFMETRIC: case SIOCSIFMETRIC:
rc = -EINVAL; break; case SIOCADDRT: case SIOCDELRT:
rc = -EPERM; if (!capable(CAP_NET_ADMIN)) break;
rc = x25_route_ioctl(cmd, argp); break; case SIOCX25GSUBSCRIP:
rc = x25_subscr_ioctl(cmd, argp); break; case SIOCX25SSUBSCRIP:
rc = -EPERM; if (!capable(CAP_NET_ADMIN)) break;
rc = x25_subscr_ioctl(cmd, argp); break; case SIOCX25GFACILITIES: {
lock_sock(sk);
rc = copy_to_user(argp, &x25->facilities, sizeof(x25->facilities))
? -EFAULT : 0;
release_sock(sk); break;
}
case SIOCX25SFACILITIES: { struct x25_facilities facilities;
rc = -EFAULT; if (copy_from_user(&facilities, argp, sizeof(facilities))) break;
rc = -EINVAL;
lock_sock(sk); if (sk->sk_state != TCP_LISTEN &&
sk->sk_state != TCP_CLOSE) goto out_fac_release; if (facilities.pacsize_in < X25_PS16 ||
facilities.pacsize_in > X25_PS4096) goto out_fac_release; if (facilities.pacsize_out < X25_PS16 ||
facilities.pacsize_out > X25_PS4096) goto out_fac_release; if (facilities.winsize_in < 1 ||
facilities.winsize_in > 127) goto out_fac_release; if (facilities.throughput) { int out = facilities.throughput & 0xf0; int in = facilities.throughput & 0x0f; if (!out)
facilities.throughput |=
X25_DEFAULT_THROUGHPUT << 4; elseif (out < 0x30 || out > 0xD0) goto out_fac_release; if (!in)
facilities.throughput |=
X25_DEFAULT_THROUGHPUT; elseif (in < 0x03 || in > 0x0D) goto out_fac_release;
} if (facilities.reverse &&
(facilities.reverse & 0x81) != 0x81) goto out_fac_release;
x25->facilities = facilities;
rc = 0;
out_fac_release:
release_sock(sk); break;
}
case SIOCX25GDTEFACILITIES: {
lock_sock(sk);
rc = copy_to_user(argp, &x25->dte_facilities, sizeof(x25->dte_facilities));
release_sock(sk); if (rc)
rc = -EFAULT; break;
}
case SIOCX25SDTEFACILITIES: { struct x25_dte_facilities dtefacs;
rc = -EFAULT; if (copy_from_user(&dtefacs, argp, sizeof(dtefacs))) break;
rc = -EINVAL;
lock_sock(sk); if (sk->sk_state != TCP_LISTEN &&
sk->sk_state != TCP_CLOSE) goto out_dtefac_release; if (dtefacs.calling_len > X25_MAX_AE_LEN) goto out_dtefac_release; if (dtefacs.called_len > X25_MAX_AE_LEN) goto out_dtefac_release;
x25->dte_facilities = dtefacs;
rc = 0;
out_dtefac_release:
release_sock(sk); break;
}
switch(cmd) { case TIOCOUTQ: case TIOCINQ:
rc = x25_ioctl(sock, cmd, (unsignedlong)argp); break; case SIOCGIFADDR: case SIOCSIFADDR: case SIOCGIFDSTADDR: case SIOCSIFDSTADDR: case SIOCGIFBRDADDR: case SIOCSIFBRDADDR: case SIOCGIFNETMASK: case SIOCSIFNETMASK: case SIOCGIFMETRIC: case SIOCSIFMETRIC:
rc = -EINVAL; break; case SIOCADDRT: case SIOCDELRT:
rc = -EPERM; if (!capable(CAP_NET_ADMIN)) break;
rc = x25_route_ioctl(cmd, argp); break; case SIOCX25GSUBSCRIP:
rc = compat_x25_subscr_ioctl(cmd, argp); break; case SIOCX25SSUBSCRIP:
rc = -EPERM; if (!capable(CAP_NET_ADMIN)) break;
rc = compat_x25_subscr_ioctl(cmd, argp); break; case SIOCX25GFACILITIES: case SIOCX25SFACILITIES: case SIOCX25GDTEFACILITIES: case SIOCX25SDTEFACILITIES: case SIOCX25GCALLUSERDATA: case SIOCX25SCALLUSERDATA: case SIOCX25GCAUSEDIAG: case SIOCX25SCAUSEDIAG: case SIOCX25SCUDMATCHLEN: case SIOCX25CALLACCPTAPPRV: case SIOCX25SENDCALLACCPT:
rc = x25_ioctl(sock, cmd, (unsignedlong)argp); break; default:
rc = -ENOIOCTLCMD; break;
} return rc;
} #endif
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.