对于客户端来说,当创建了一个套接字后,就可以连接它了。
case SYS_CONNECT:
err = sys_connect(a0, (struct sockaddr __user )a1, a[2]);
break;[/code]
asmlinkage long sys_connect(int fd, struct sockaddr __user uservaddr, int addrlen)
{
struct socket sock;
char address[MAX_SOCK_ADDR];
int err;
sock = sockfd_lookup(fd, &err);
if (!sock)
goto out;
err = move_addr_to_kernel(uservaddr, addrlen, address);
if (err < 0)
goto out_put;
err = security_socket_connect(sock, (struct sockaddr )address, addrlen);
if (err) goto out_put;
err = sock->ops->connect(sock, (struct sockaddr ) address, addrlen,
sock->file->f_flags);
out_put:
sockfd_put(sock);
out:
return err;
}
跟其它 *** 作类似,sys_connect 接着调用 inet_connect:
/
Connect to a remote host There is regrettably still a little
TCP 'magic' in here
/
int inet_stream_connect(struct socket sock, struct sockaddr uaddr,
int addr_len, int flags)
{
struct sock sk = sock->sk;
int err;
long timeo;
lock_sock(sk);
if (uaddr->sa_family == AF_UNSPEC) {
err = sk->sk_prot->disconnect(sk, flags);
sock->state = err SS_DISCONNECTING : SS_UNCONNECTED;
goto out;
}
提交的协议簇不正确,则断开连接。
switch (sock->state) {
default:
err = -EINVAL;
goto out;
case SS_CONNECTED:
err = -EISCONN;
goto out;
case SS_CONNECTING:
err = -EALREADY;
/ Fall out of switch with err, set for this state /
break;[/code] socket 处于不正确的连接状态,返回相应的错误值。
case SS_UNCONNECTED:
err = -EISCONN;
if (sk->sk_state != TCP_CLOSE)
goto out;
/调用协议的连接函数/
err = sk->sk_prot->connect(sk, uaddr, addr_len);
if (err < 0)
goto out;
/协议方面的工作已经处理完成了,但是自己的一切工作还没有完成,所以切换至正在连接中/
sock->state = SS_CONNECTING;
/ Just entered SS_CONNECTING state; the only
difference is that return value in non-blocking
case is EINPROGRESS, rather than EALREADY
/
err = -EINPROGRESS;
break;
}
对于 TCP的实际的连接,是通过调用 tcp_v4_connect()函数来实现的。
二、tcp_v4_connect函数
对于 TCP 协议来说,其连接,实际上就是发送一个 SYN 报文,在服务器的应到到来时,回答它一个 ack 报文,也就是完成三次握手中的第一和第三次。
要发送 SYN 报文,也就是说,需要有完整的来源/目的地址,来源/目的端口,目的地址/端口由用户态提交,但是问题是没有自己的地址和端口,因为并没有调 用过 bind(2),一台主机,对于端口,可以像 sys_bind()那样,从本地未用端口中动态分配一个,那地址呢?因为一台主机可能会存在多个 IP地 址,如果随机动态选择,那么有可能选择一个错误的来源地址,将不能正确地到达目的地址。换句话说,来源地址的选择,是与路由相关的。
调用路由查找的核心函数 ip_route_output_slow(),在没有提供来源地址的情况下,会根据实际情况,调用 inet_select_addr()函数来选择一个合适的。同时,如果路由查找命中,会生成一个相应的路由缓存项,这个缓存项,不但对当前发送 SYN 报 文有意义,对于后续的所有数据包,都可以起到一个加速路由查找的作用。这一任务,是通过 ip_route_connect()函数完成的,它返回相应的路由缓存项(也就是说,来源地址也在其中了):
static inline int ip_route_connect(struct rtable rp, u32 dst,
u32 src, u32 tos, int oif, u8 protocol,
u16 sport, u16 dport, struct sock sk)
{ struct flowi fl = { oif = oif,
nl_u = { ip4_u = { daddr = dst,
saddr = src,
tos = tos } },
proto = protocol,
uli_u = { ports =
{ sport = sport,
dport = dport } } };
int err;
if (!dst || !src) {
err = __ip_route_output_key(rp, &fl);
if (err)
return err;
flfl4_dst = (rp)->rt_dst;
flfl4_src = (rp)->rt_src;
ip_rt_put(rp);
rp = NULL;
}
return ip_route_output_flow(rp, &fl, sk, 0);
}
首先,构建一个搜索 key fl,在搜索要素中,来源地址/端口是不存在的。所以,当通过__ip_route_output_key 进行查找时,第一次是不会命中缓存的。 __ip_route_output_key 将继续调用ip_route_output_slow()函数,在路由表中搜索,并返回一个合适的来源地址, 并且生成一个路由缓存项。 路由查找的更多细节,我会在另一个贴子中来分析。
/ This will initiate an outgoing connection /
int tcp_v4_connect(struct sock sk, struct sockaddr uaddr, int addr_len)
{
struct inet_sock inet = inet_sk(sk);
struct tcp_sock tp = tcp_sk(sk);
struct sockaddr_in usin = (struct sockaddr_in )uaddr;
struct rtable rt;
u32 daddr, nexthop;
int tmp;
int err;
if (addr_len < sizeof(struct sockaddr_in))
return -EINVAL;
if (usin->sin_family != AF_INET)
return -EAFNOSUPPORT;
校验地址长度和协议簇。
nexthop = daddr = usin->sin_addrs_addr;
将下一跳地址和目的地址的临时变量都暂时设为用户提交的地址。
if (inet->opt && inet->opt->srr) {
if (!daddr)
return -EINVAL;
nexthop = inet->opt->faddr;
}
如果使用了来源地址路由,选择一个合适的下一跳地址。
tmp = ip_route_connect(&rt, nexthop, inet->saddr,
RT_CONN_FLAGS(sk), sk->sk_bound_dev_if,
IPPROTO_TCP,
inet->sport, usin->sin_port, sk);
if (tmp < 0)
return tmp;
if (rt->rt_flags & (RTCF_MULTICAST | RTCF_BROADCAST)) {
ip_rt_put(rt);
return -ENETUNREACH;
}
进行路由查找,并校验返回的路由的类型,TCP是不被允许使用多播和广播的。
if (!inet->opt || !inet->opt->srr)
daddr = rt->rt_dst;
更新目的地址临时变量——使用路由查找后返回的值。
if (!inet->saddr)
inet->saddr = rt->rt_src;
inet->rcv_saddr = inet->saddr;
如果还没有设置源地址,和本地发送地址,则使用路由中返回的值。
if (tp->rx_optts_recent_stamp && inet->daddr != daddr) {
/ Reset inherited state /
tp->rx_optts_recent = 0;
tp->rx_optts_recent_stamp = 0;
tp->write_seq = 0;
}
if (sysctl_tcp_tw_recycle &&
!tp->rx_optts_recent_stamp && rt->rt_dst == daddr) {
struct inet_peer peer = rt_get_peer(rt);
/ VJ's idea We save last timestamp seen from
the destination in peer table, when entering state TIME-WAIT
and initialize rx_optts_recent from it, when trying new connection
/
if (peer && peer->tcp_ts_stamp + TCP_PAWS_MSL >= xtimetv_sec) {
tp->rx_optts_recent_stamp = peer->tcp_ts_stamp;
tp->rx_optts_recent = peer->tcp_ts;
}
}
这个更新初始状态方面的内容,还没有去分析它。
inet->dport = usin->sin_port;
inet->daddr = daddr;
保存目的地址及端口。
tp->ext_header_len = 0;
if (inet->opt)
tp->ext_header_len = inet->opt->optlen;
tp->rx_optmss_clamp = 536;
设置最小允许的 mss 值
tcp_set_state(sk, TCP_SYN_SENT);
套接字状态被置为 TCP_SYN_SENT,
err = tcp_v4_hash_connect(sk);
if (err)
goto failure;
动态选择一个本地端口,并加入 hash 表,与bind(2)选择端口类似。
err = ip_route_newports(&rt, inet->sport, inet->dport, sk);
if (err)
goto failure;
/ OK, now commit destination to socket /
__sk_dst_set(sk, &rt->udst);
tcp_v4_setup_caps(sk, &rt->udst);
因为本地端口已经改变,使用新端口,重新查找路由,并用新的路由缓存项更新 sk 中保存的路由缓存项。
if (!tp->write_seq)
tp->write_seq = secure_tcp_sequence_number(inet->saddr,
inet->daddr,
inet->sport,
usin->sin_port);
为 TCP报文计算一个 seq值(实际使用的值是 tp->write_seq+1)。
inet->id = tp->write_seq ^ jiffies;
err = tcp_connect(sk);
rt = NULL;
if (err)
goto failure;
return 0;
tp_connect()函数用来根据 sk 中的信息,构建一个完成的 syn 报文,并将它发送出去。在分析 tcp栈的实现时再来分析它。
根据 TCP协议,接下来的问题是,
1、可能收到了服务器的应答,则要回送一个 ack 报文;
2、如果超时还没有应答,则使用超时重发定时器;在介绍安装过程的时候只有使用文本方式,就不附图了。
首先将下载的光盘镜像或安装软盘制作成盘,在cmos中设置好启动顺序后从光盘或软盘启动。第一步安装程序会让你选择需要安装的组件,选择好后按i开始安装,然后一路按y即可完成。
安装完成后第一次进入会要求输入注册码,这时使用算号器,在算号器上面一栏输入机器码即可得到注册码(注意输入的大小写)。第一次进入的默认帐号为:admin,密码为空。
基本的设置:(以下本人以telnet及该路由自带的winbox来说明)
1、设置网卡(这一步需要在本机执行,不设置好ip则telnet等工具是无法使用的)
输入set设置网卡,在enable interface这行输入网卡名称(第一次可以用ether1或ether2等代表第一张及第二张网卡);在ip address这行输入该网卡ip(可以为一张网卡绑定多个ip);在netmask这行输入子网掩码。
routeos一个强大的地方在于其可以使用多张网卡,而且可以自由地将这些网卡指定为不同的网段(无论是外网还是内网)
所有网卡set完以后,就可以用telnet登录这个路由了,或者你也可以使用路由ip来访问该路由,在这个路由自带网页上带了一个叫做winbox的工具,可以用这个工具来访问路由。
设置好网卡ip的routeos还是不能让局域网的机器上网的,要使局域网机器上网还需要设置ip欺骗。
设置ip欺骗可以使用以下两句:/ip firewall src-nat add action masquerade /ip firewall mangle add protocol tcp tcp-options syn-only tcp-mss 1448
配置路由使用以下语句:/ip route add dst 0000/0 gat 公网网关ip配置adsl线路使用以下语句:/interface pppoe-client add name 随意 service 服务器名(随意) user 拔号用户名 password 拔号密码 interface 绑定网卡名 use-peer-dns yes mtu 1492 mru 1492
RouterOS的端口映射很简单。
在终端中的ip/firewall/dst-nat里面比如我要映射80端口到1921680100,外网地址是21826xx(假设)
输入add action=nat protocol=tcp dst-address=21826xx/32:80 to-dst-address=1921680100就ok了,需要注意的是做nat的时候,dst-address的子网掩码必须是32,也就是255255255255,否则不行,很多朋友出问题就是出在这里。给你看个教程。不少公司的网管试图解决双网卡问题,下面我就给大家详细的讲解一下双网卡同时使用的方法,这样即可保障内网的安全,又能解决电脑访问外网的问题,一举两得。希望大家喜欢。
首先你的机器需要有两块网卡,分别接到两台交换机上,
internet地址:19216818,子网掩码:2552552550,网关:19216811
内部网地址:1722318,子网掩码:2552552550,网关:1722311
如果按正常的设置方法设置每块网卡的ip地址和网关,再cmd下使用route print查看时会看到
Network Destination Netmask Gateway Interface Metric
0000 0000 19216811 19216818
0000 0000 1722311 1722318
即指向0000的有两个网关,这样就会出现路由冲突,两个网络都不能访问。
如何实现同时访问两个网络?那要用到route命令
第一步:route delete 0000 "删除所有0000的路由"
第二步:route add 0000 mask 0000 19216811 "添加0000网络路由"这个是主要的,意思就是你可以上外网
第三步:route add 1722300 mask 255000 1722311 "添加1722300网络路由",注意mask为255000 ,而不是2552552550 ,这样内部的多网段才可用。
这时就可以同时访问两个网络了,但碰到一个问题,使用上述命令添加的路由在系统重新启动后会自动丢失,怎样保存现有的路由表呢?
route add -p 添加静态路由,即重启后,路由不会丢失。注意使用前要在tcp/ip设置里去掉接在企业内部网的网卡的网关
------------------------------------------------------------------------------------------------
一些单位将内网和外网分开了。痛苦啊,偶单位就是如此。boss当然是基于安全性考虑了,可是没有笔记本的怎么办?又要办公,有得上网。没办法,发扬DIY精神偷偷装一块网卡,让聊天与工作同在。让你的主机内外兼顾。这是我在网上找到的,谢谢作者了。方法如下:
1设置其中接internet的网卡的网关为10001,启用后就是默认网关
--注:这是对应外网的网卡,按照你们单位外网的ip分配情况,在TCP/IP属性中配置好 ip、掩码、DNS
2将连接单位内部网的网卡IP配好后,设网关设置为空(即不设网关),启用后,此时内网无法通过网关路由
3进入CMD,运行:route -p add 192000 mask 255000 19216801 metric 1
--注:意思是将192的IP包的路由网关设为19216801 ,-P 参数代表永久写入路由表,建议先不加此参数,实践通过后在写上去
4 OK!同时启用两个网卡,两个网关可以同时起作用了,两个子网也可以同时访问了,关机重启后也不用重设!
其实这是个中折的办法。。。。使大家的双网卡同时运行,很不错的方法。大家学习一下吧。。
还有 :开机的时候设置一下本地的路由表
把下面的命令写到一个bat里面,开机运行一下
route ADD [内网网的网络地址] MASK [内网的掩码] [通向内网的网关] METRIC 1
route ADD 0000 MASK 0000 [通向Internet的网关] METRIC 1
[]内的内容需要你修改成适合你网络的参数(修改后[]不保留),其他内容不用动
这个方法没有停用任何一块网卡,两个网卡还在同时工作,只是数据包的投递方向发生了改变,这样设置以后可以保证流向财务网的数据不向internet发送,流向internet的数据也不向财务网发送,各走各的路!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)