📄 sock.c
字号:
/*
* INET An implementation of the TCP/IP protocol suite for the LINUX
* operating system. INET is implemented using the BSD Socket
* interface as the means of communication with the user level.
*
* SOCK - AF_INET protocol family socket handler.
*
* Version: @(#)sock.c 1.0.17 06/02/93
*
* Authors: Ross Biro, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
* Florian La Roche, <flla@stud.uni-sb.de>
*
* Fixes:
* Alan Cox : Numerous verify_area() problems
* Alan Cox : Connecting on a connecting socket
* now returns an error for tcp.
* Alan Cox : sock->protocol is set correctly.
* and is not sometimes left as 0.
* Alan Cox : connect handles icmp errors on a
* connect properly. Unfortunately there
* is a restart syscall nasty there. I
* can't match BSD without hacking the C
* library. Ideas urgently sought!
* Alan Cox : Disallow bind() to addresses that are
* not ours - especially broadcast ones!!
* Alan Cox : Socket 1024 _IS_ ok for users. (fencepost)
* Alan Cox : sock_wfree/sock_rfree don't destroy sockets,
* instead they leave that for the DESTROY timer.
* Alan Cox : Clean up error flag in accept
* Alan Cox : TCP ack handling is buggy, the DESTROY timer
* was buggy. Put a remove_sock() in the handler
* for memory when we hit 0. Also altered the timer
* code. The ACK stuff can wait and needs major
* TCP layer surgery.
* Alan Cox : Fixed TCP ack bug, removed remove sock
* and fixed timer/inet_bh race.
* Alan Cox : Added zapped flag for TCP
* Alan Cox : Move kfree_skb into skbuff.c and tidied up surplus code
* Alan Cox : for new sk_buff allocations wmalloc/rmalloc now call alloc_skb
* Alan Cox : kfree_s calls now are kfree_skbmem so we can track skb resources
* Alan Cox : Supports socket option broadcast now as does udp. Packet and raw need fixing.
* Alan Cox : Added RCVBUF,SNDBUF size setting. It suddenely occured to me how easy it was so...
* Rick Sladkey : Relaxed UDP rules for matching packets.
* C.E.Hawkins : IFF_PROMISC/SIOCGHWADDR support
* Pauline Middelink : Pidentd support
* Alan Cox : Fixed connect() taking signals I think.
* Alan Cox : SO_LINGER supported
* Alan Cox : Error reporting fixes
* Anonymous : inet_create tidied up (sk->reuse setting)
* Alan Cox : inet sockets don't set sk->type!
* Alan Cox : Split socket option code
* Alan Cox : Callbacks
* Alan Cox : Nagle flag for Charles & Johannes stuff
*
* To Fix:
*
*
* This program 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.
*/
#include <linux/config.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/in.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/string.h>
#include <linux/sockios.h>
#include <linux/net.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <asm/segment.h>
#include <asm/system.h>
#include "inet.h"
#include "dev.h"
#include "ip.h"
#include "protocol.h"
#include "arp.h"
#include "route.h"
#include "tcp.h"
#include "udp.h"
#include "skbuff.h"
#include "sock.h"
#include "raw.h"
#include "icmp.h"
int inet_debug = DBG_OFF; /* INET module debug flag */
#define min(a,b) ((a)<(b)?(a):(b))
extern struct proto packet_prot;
void
print_sk(struct sock *sk)
{
if (!sk) {
printk(" print_sk(NULL)\n");
return;
}
printk(" wmem_alloc = %lu\n", sk->wmem_alloc);
printk(" rmem_alloc = %lu\n", sk->rmem_alloc);
printk(" send_head = %p\n", sk->send_head);
printk(" state = %d\n",sk->state);
printk(" wback = %p, rqueue = %p\n", sk->wback, sk->rqueue);
printk(" wfront = %p\n", sk->wfront);
printk(" daddr = %lX, saddr = %lX\n", sk->daddr,sk->saddr);
printk(" num = %d", sk->num);
printk(" next = %p\n", sk->next);
printk(" write_seq = %ld, acked_seq = %ld, copied_seq = %ld\n",
sk->write_seq, sk->acked_seq, sk->copied_seq);
printk(" rcv_ack_seq = %ld, window_seq = %ld, fin_seq = %ld\n",
sk->rcv_ack_seq, sk->window_seq, sk->fin_seq);
printk(" prot = %p\n", sk->prot);
printk(" pair = %p, back_log = %p\n", sk->pair,sk->back_log);
printk(" inuse = %d , blog = %d\n", sk->inuse, sk->blog);
printk(" dead = %d delay_acks=%d\n", sk->dead, sk->delay_acks);
printk(" retransmits = %ld, timeout = %d\n", sk->retransmits, sk->timeout);
printk(" cong_window = %d, packets_out = %d\n", sk->cong_window,
sk->packets_out);
printk(" shutdown=%d\n", sk->shutdown);
}
void
print_skb(struct sk_buff *skb)
{
if (!skb) {
printk(" print_skb(NULL)\n");
return;
}
printk(" prev = %p, next = %p\n", skb->prev, skb->next);
printk(" sk = %p link3 = %p\n", skb->sk, skb->link3);
printk(" mem_addr = %p, mem_len = %lu\n", skb->mem_addr, skb->mem_len);
printk(" used = %d free = %d\n", skb->used,skb->free);
}
static int
sk_inuse(struct proto *prot, int num)
{
struct sock *sk;
for(sk = prot->sock_array[num & (SOCK_ARRAY_SIZE -1 )];
sk != NULL;
sk=sk->next) {
if (sk->num == num) return(1);
}
return(0);
}
unsigned short
get_new_socknum(struct proto *prot, unsigned short base)
{
static int start=0;
/*
* Used to cycle through the port numbers so the
* chances of a confused connection drop.
*/
int i, j;
int best = 0;
int size = 32767; /* a big num. */
struct sock *sk;
if (base == 0) base = PROT_SOCK+1+(start % 1024);
if (base <= PROT_SOCK) {
base += PROT_SOCK+(start % 1024);
}
/* Now look through the entire array and try to find an empty ptr. */
for(i=0; i < SOCK_ARRAY_SIZE; i++) {
j = 0;
sk = prot->sock_array[(i+base+1) &(SOCK_ARRAY_SIZE -1)];
while(sk != NULL) {
sk = sk->next;
j++;
}
if (j == 0) {
start =(i+1+start )%1024;
DPRINTF((DBG_INET, "get_new_socknum returning %d, start = %d\n",
i + base + 1, start));
return(i+base+1);
}
if (j < size) {
best = i;
size = j;
}
}
/* Now make sure the one we want is not in use. */
while(sk_inuse(prot, base +best+1)) {
best += SOCK_ARRAY_SIZE;
}
DPRINTF((DBG_INET, "get_new_socknum returning %d, start = %d\n",
best + base + 1, start));
return(best+base+1);
}
void
put_sock(unsigned short num, struct sock *sk)
{
struct sock *sk1;
struct sock *sk2;
int mask;
DPRINTF((DBG_INET, "put_sock(num = %d, sk = %X\n", num, sk));
sk->num = num;
sk->next = NULL;
num = num &(SOCK_ARRAY_SIZE -1);
/* We can't have an interupt re-enter here. */
cli();
if (sk->prot->sock_array[num] == NULL) {
sk->prot->sock_array[num] = sk;
sti();
return;
}
sti();
for(mask = 0xff000000; mask != 0xffffffff; mask = (mask >> 8) | mask) {
if ((mask & sk->saddr) &&
(mask & sk->saddr) != (mask & 0xffffffff)) {
mask = mask << 8;
break;
}
}
DPRINTF((DBG_INET, "mask = %X\n", mask));
cli();
sk1 = sk->prot->sock_array[num];
for(sk2 = sk1; sk2 != NULL; sk2=sk2->next) {
if (!(sk2->saddr & mask)) {
if (sk2 == sk1) {
sk->next = sk->prot->sock_array[num];
sk->prot->sock_array[num] = sk;
sti();
return;
}
sk->next = sk2;
sk1->next= sk;
sti();
return;
}
sk1 = sk2;
}
/* Goes at the end. */
sk->next = NULL;
sk1->next = sk;
sti();
}
static void
remove_sock(struct sock *sk1)
{
struct sock *sk2;
DPRINTF((DBG_INET, "remove_sock(sk1=%X)\n", sk1));
if (!sk1) {
printk("sock.c: remove_sock: sk1 == NULL\n");
return;
}
if (!sk1->prot) {
printk("sock.c: remove_sock: sk1->prot == NULL\n");
return;
}
/* We can't have this changing out from under us. */
cli();
sk2 = sk1->prot->sock_array[sk1->num &(SOCK_ARRAY_SIZE -1)];
if (sk2 == sk1) {
sk1->prot->sock_array[sk1->num &(SOCK_ARRAY_SIZE -1)] = sk1->next;
sti();
return;
}
while(sk2 && sk2->next != sk1) {
sk2 = sk2->next;
}
if (sk2) {
sk2->next = sk1->next;
sti();
return;
}
sti();
if (sk1->num != 0) DPRINTF((DBG_INET, "remove_sock: sock not found.\n"));
}
void
destroy_sock(struct sock *sk)
{
struct sk_buff *skb;
DPRINTF((DBG_INET, "destroying socket %X\n", sk));
sk->inuse = 1; /* just to be safe. */
/* Incase it's sleeping somewhere. */
if (!sk->dead)
sk->write_space(sk);
remove_sock(sk);
/* Now we can no longer get new packets. */
delete_timer(sk);
while ((skb = tcp_dequeue_partial(sk)) != NULL)
{
IS_SKB(skb);
kfree_skb(skb, FREE_WRITE);
}
/* Cleanup up the write buffer. */
for(skb = sk->wfront; skb != NULL; )
{
struct sk_buff *skb2;
skb2=(struct sk_buff *)skb->next;
if (skb->magic != TCP_WRITE_QUEUE_MAGIC) {
printk("sock.c:destroy_sock write queue with bad magic(%X)\n",
skb->magic);
break;
}
IS_SKB(skb);
kfree_skb(skb, FREE_WRITE);
skb = skb2;
}
sk->wfront = NULL;
sk->wback = NULL;
if (sk->rqueue != NULL)
{
while((skb=skb_dequeue(&sk->rqueue))!=NULL)
{
/*
* This will take care of closing sockets that were
* listening and didn't accept everything.
*/
if (skb->sk != NULL && skb->sk != sk)
{
IS_SKB(skb);
skb->sk->dead = 1;
skb->sk->prot->close(skb->sk, 0);
}
IS_SKB(skb);
kfree_skb(skb, FREE_READ);
}
}
sk->rqueue = NULL;
/* Now we need to clean up the send head. */
for(skb = sk->send_head; skb != NULL; )
{
struct sk_buff *skb2;
/*
* We need to remove skb from the transmit queue,
* or maybe the arp queue.
*/
cli();
/* see if it's in a transmit queue. */
/* this can be simplified quite a bit. Look */
/* at tcp.c:tcp_ack to see how. */
if (skb->next != NULL)
{
IS_SKB(skb);
skb_unlink(skb);
}
skb->dev = NULL;
sti();
skb2 = (struct sk_buff *)skb->link3;
kfree_skb(skb, FREE_WRITE);
skb = skb2;
}
sk->send_head = NULL;
/* And now the backlog. */
if (sk->back_log != NULL)
{
/* this should never happen. */
printk("cleaning back_log. \n");
cli();
skb = (struct sk_buff *)sk->back_log;
do
{
struct sk_buff *skb2;
skb2 = (struct sk_buff *)skb->next;
kfree_skb(skb, FREE_READ);
skb = skb2;
}
while(skb != sk->back_log);
sti();
}
sk->back_log = NULL;
/* Now if it has a half accepted/ closed socket. */
if (sk->pair)
{
sk->pair->dead = 1;
sk->pair->prot->close(sk->pair, 0);
sk->pair = NULL;
}
/*
* Now if everything is gone we can free the socket
* structure, otherwise we need to keep it around until
* everything is gone.
*/
if (sk->rmem_alloc == 0 && sk->wmem_alloc == 0)
{
kfree_s((void *)sk,sizeof(*sk));
}
else
{
/* this should never happen. */
/* actually it can if an ack has just been sent. */
DPRINTF((DBG_INET, "possible memory leak in socket = %X\n", sk));
sk->destroy = 1;
sk->ack_backlog = 0;
sk->inuse = 0;
reset_timer(sk, TIME_DESTROY, SOCK_DESTROY_TIME);
}
DPRINTF((DBG_INET, "leaving destroy_sock\n"));
}
static int
inet_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
struct sock *sk;
sk = (struct sock *) sock->data;
if (sk == NULL) {
printk("Warning: sock->data = NULL: %d\n" ,__LINE__);
return(0);
}
switch(cmd) {
case F_SETOWN:
/*
* This is a little restrictive, but it's the only
* way to make sure that you can't send a sigurg to
* another process.
*/
if (!suser() && current->pgrp != -arg &&
current->pid != arg) return(-EPERM);
sk->proc = arg;
return(0);
case F_GETOWN:
return(sk->proc);
default:
return(-EINVAL);
}
}
/*
* Set socket options on an inet socket.
*/
static int inet_setsockopt(struct socket *sock, int level, int optname,
char *optval, int optlen)
{
struct sock *sk = (struct sock *) sock->data;
if (level == SOL_SOCKET)
return sock_setsockopt(sk,level,optname,optval,optlen);
if (sk->prot->setsockopt==NULL)
return(-EOPNOTSUPP);
else
return sk->prot->setsockopt(sk,level,optname,optval,optlen);
}
static int inet_getsockopt(struct socket *sock, int level, int optname,
char *optval, int *optlen)
{
struct sock *sk = (struct sock *) sock->data;
if (level == SOL_SOCKET)
return sock_getsockopt(sk,level,optname,optval,optlen);
if(sk->prot->getsockopt==NULL)
return(-EOPNOTSUPP);
else
return sk->prot->getsockopt(sk,level,optname,optval,optlen);
}
/*
* This is meant for all protocols to use and covers goings on
* at the socket level. Everything here is generic.
*/
int sock_setsockopt(struct sock *sk, int level, int optname,
char *optval, int optlen)
{
int val;
int err;
struct linger ling;
if (optval == NULL)
return(-EINVAL);
err=verify_area(VERIFY_READ, optval, sizeof(int));
if(err)
return err;
val = get_fs_long((unsigned long *)optval);
switch(optname)
{
case SO_TYPE:
case SO_ERROR:
return(-ENOPROTOOPT);
case SO_DEBUG:
sk->debug=val?1:0;
case SO_DONTROUTE: /* Still to be implemented */
return(0);
case SO_BROADCAST:
sk->broadcast=val?1:0;
return 0;
case SO_SNDBUF:
if(val>32767)
val=32767;
if(val<256)
val=256;
sk->sndbuf=val;
return 0;
case SO_LINGER:
err=verify_area(VERIFY_READ,optval,sizeof(ling));
if(err)
return err;
memcpy_fromfs(&ling,optval,sizeof(ling));
if(ling.l_onoff==0)
sk->linger=0;
else
{
sk->lingertime=ling.l_linger;
sk->linger=1;
}
return 0;
case SO_RCVBUF:
if(val>32767)
val=32767;
if(val<256)
val=256;
sk->rcvbuf=val;
return(0);
case SO_REUSEADDR:
if (val)
sk->reuse = 1;
else
sk->reuse = 0;
return(0);
case SO_KEEPALIVE:
if (val)
sk->keepopen = 1;
else
sk->keepopen = 0;
return(0);
case SO_OOBINLINE:
if (val)
sk->urginline = 1;
else
sk->urginline = 0;
return(0);
case SO_NO_CHECK:
if (val)
sk->no_check = 1;
else
sk->no_check = 0;
return(0);
case SO_PRIORITY:
if (val >= 0 && val < DEV_NUMBUFFS)
{
sk->priority = val;
}
else
{
return(-EINVAL);
}
return(0);
default:
return(-ENOPROTOOPT);
}
}
int sock_getsockopt(struct sock *sk, int level, int optname,
char *optval, int *optlen)
{
int val;
int err;
struct linger ling;
switch(optname)
{
case SO_DEBUG:
val = sk->debug;
break;
case SO_DONTROUTE: /* One last option to implement */
val = 0;
break;
case SO_BROADCAST:
val= sk->broadcast;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -