struct proto_ops inet6_stream_ops = {				: struct socket -> ops
	family:		PF_INET6,

	release:	inet6_release,
	bind:		inet6_bind,
	connect:	inet_stream_connect,		/* ok		*/
	socketpair:	sock_no_socketpair,		/* a do nothing	*/
	accept:		inet_accept,			/* ok		*/
	getname:	inet6_getname, 
	poll:		tcp_poll,			/* ok		*/
	ioctl:		inet6_ioctl,			/* must change  */
	listen:		inet_listen,			/* ok		*/
	shutdown:	inet_shutdown,			/* ok		*/
	setsockopt:	inet_setsockopt,		/* ok		*/
	getsockopt:	inet_getsockopt,		/* ok		*/
	sendmsg:	inet_sendmsg,			/* ok		*/
	recvmsg:	inet_recvmsg,			/* ok		*/
	mmap:		sock_no_mmap,
	sendpage:	tcp_sendpage
};

struct proto_ops inet6_dgram_ops = {
	family:		PF_INET6,

	release:	inet6_release,
	bind:		inet6_bind,
	connect:	inet_dgram_connect,		/* ok		*/
	socketpair:	sock_no_socketpair,		/* a do nothing	*/
	accept:		sock_no_accept,			/* a do nothing	*/
	getname:	inet6_getname, 
	poll:		datagram_poll,			/* ok		*/
	ioctl:		inet6_ioctl,			/* must change  */
	listen:		sock_no_listen,			/* ok		*/
	shutdown:	inet_shutdown,			/* ok		*/
	setsockopt:	inet_setsockopt,		/* ok		*/
	getsockopt:	inet_getsockopt,		/* ok		*/
	sendmsg:	inet_sendmsg,			/* ok		*/
	recvmsg:	inet_recvmsg,			/* ok		*/
	mmap:		sock_no_mmap,
	sendpage:	sock_no_sendpage,
};

struct proto tcpv6_prot = {					: struct sock -> prot
	name:		"TCPv6",
	close:		tcp_close,
	connect:	tcp_v6_connect,
	disconnect:	tcp_disconnect,
	accept:		tcp_accept,
	ioctl:		tcp_ioctl,
	init:		tcp_v6_init_sock,
	destroy:	tcp_v6_destroy_sock,
	shutdown:	tcp_shutdown,
	setsockopt:	tcp_setsockopt,
	getsockopt:	tcp_getsockopt,
	sendmsg:	tcp_sendmsg,
	recvmsg:	tcp_recvmsg,
	backlog_rcv:	tcp_v6_do_rcv,
	hash:		tcp_v6_hash,
	unhash:		tcp_unhash,
	get_port:	tcp_v6_get_port,
};

asmlinkage long sys_getpeername(int fd, struct sockaddr *usockaddr, int *usockaddr_len)
{
	struct socket *sock;
	char address[MAX_SOCK_ADDR];
	int len, err;
- if ((sock = sockfd_lookup(fd, &err))!=NULL) {
  - err = sock->ops->getname(sock, (struct sockaddr *)address, &len, 1);	: sys_getsockname() ǤϺǸΰ 0
  - if (!err) err=move_addr_to_user(address,len, usockaddr, usockaddr_len);
  - sockfd_put(sock);
- }
- return err;
}

static int inet6_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, int peer)
{
	struct sockaddr_in6 *sin=(struct sockaddr_in6 *)uaddr;
	struct sock *sk = sock->sk;
- sin->sin6_family = AF_INET6;
- sin->sin6_flowinfo = 0;
- sin->sin6_scope_id = 0;
- if (peer) {									: sys_getpeername() νsys_accept()  2
  - if (!sk->dport) return -ENOTCONN;
  - if (((1<<sk->state)&(TCPF_CLOSE|TCPF_SYN_SENT)) && peer == 1) return -ENOTCONN;
  - sin->sin6_port = sk->dport;
  - ipv6_addr_copy(&sin->sin6_addr, &sk->net_pinfo.af_inet6.daddr);
  - if (sk->net_pinfo.af_inet6.sndflow)						: Ǽե٥ϼΤǤϤʤ
    - sin->sin6_flowinfo = sk->net_pinfo.af_inet6.flow_label;			: sock ˤĤե٥ sndflow Ȥ
- } else {									: sys_getsockname() ν
  - if (ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr) == IPV6_ADDR_ANY)
    - ipv6_addr_copy(&sin->sin6_addr, &sk->net_pinfo.af_inet6.saddr);
  - else
    - ipv6_addr_copy(&sin->sin6_addr, &sk->net_pinfo.af_inet6.rcv_saddr);
  - sin->sin6_port = sk->sport;
- }
- if (ipv6_addr_type(&sin->sin6_addr) & IPV6_ADDR_LINKLOCAL) sin->sin6_scope_id = sk->bound_dev_if;
- *uaddr_len = sizeof(*sin);
- return(0);
}

static int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
	struct sock *sk = sock->sk;
	int err = -EINVAL;
	int pid;

	switch(cmd) 
	{
	case FIOSETOWN:
	case SIOCSPGRP:
		if (get_user(pid, (int *) arg))
			return -EFAULT;
		/* see sock_no_fcntl */
		if (current->pid != pid && current->pgrp != -pid && 
		    !capable(CAP_NET_ADMIN))
			return -EPERM;
		sk->proc = pid;
		return(0);
	case FIOGETOWN:
	case SIOCGPGRP:
		return put_user(sk->proc,(int *)arg);
	case SIOCGSTAMP:
		if(sk->stamp.tv_sec==0)
			return -ENOENT;
		err = copy_to_user((void *)arg, &sk->stamp,
				   sizeof(struct timeval));
		if (err)
			return -EFAULT;
		return 0;

	case SIOCADDRT:
	case SIOCDELRT:
	  
		return(ipv6_route_ioctl(cmd,(void *)arg));

	case SIOCSIFADDR:
		return addrconf_add_ifaddr((void *) arg);
	case SIOCDIFADDR:
		return addrconf_del_ifaddr((void *) arg);
	case SIOCSIFDSTADDR:
		return addrconf_set_dstaddr((void *) arg);
	default:
		if ((cmd >= SIOCDEVPRIVATE) &&
		    (cmd <= (SIOCDEVPRIVATE + 15)))
			return(dev_ioctl(cmd,(void *) arg));
		
		if(sk->prot->ioctl==0 || (err=sk->prot->ioctl(sk, cmd, arg))==-ENOIOCTLCMD)
			return(dev_ioctl(cmd,(void *) arg));		
		return err;
	}
	/*NOTREACHED*/
	return(0);
}

int tcp_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen)
{
	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
	int val;
	int err = 0;
- if (level != SOL_TCP)
  - return tp->af_specific->setsockopt(sk, level, optname, optval, optlen);
...
}

static struct tcp_func ipv6_specific = {
	tcp_v6_xmit,
	tcp_v6_send_check,
	tcp_v6_rebuild_header,
	tcp_v6_conn_request,
	tcp_v6_syn_recv_sock,
	tcp_v6_remember_stamp,
	sizeof(struct ipv6hdr),

	ipv6_setsockopt,
	ipv6_getsockopt,
	v6_addr2sockaddr,
	sizeof(struct sockaddr_in6)
};

/*
 *	TCP over IPv4 via INET6 API
 */

static struct tcp_func ipv6_mapped = {
	ip_queue_xmit,
	tcp_v4_send_check,
	tcp_v4_rebuild_header,
	tcp_v6_conn_request,
	tcp_v6_syn_recv_sock,
	tcp_v4_remember_stamp,
	sizeof(struct iphdr),

	ipv6_setsockopt,
	ipv6_getsockopt,
	v6_addr2sockaddr,
	sizeof(struct sockaddr_in6)
};

