📄 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;voidprint_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);}voidprint_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 intsk_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 shortget_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);}voidput_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 voidremove_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"));}voiddestroy_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 intinet_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 + -