linux临时端口,临时端口号(ephemeral port)的动态分配

网络端口号是如何分配的?除了给常用服务保留的Well-known Port numbers之外,给客户端的端口号通常是动态分配的,称为ephemeral port(临时端口),在Linux系统上临时端口号的取值范围是通过这个内核参数定义的:net.ipv4.ip_local_port_range (/proc/sys/net/ipv4/ip_local_port_range),端口号动态分配时并不是从小到大依次选取的,而是按照特定的算法随机分配的。

临时端口号的分配发生在以下两处:

– bind();

– connect()。

bind()通过inet_csk_get_port()获取端口号,利用了net_random()产生的随机数 :

// SLES12 kernel 3.12.69-60.64.32:

0104 int inet_csk_get_port(struct sock *sk, unsigned short snum)

0105 {

0106 struct inet_hashinfo *hashinfo = sk->sk_prot->h.hashinfo;

0107 struct inet_bind_hashbucket *head;

0108 struct inet_bind_bucket *tb;

0109 int ret, attempts = 5;

0110 struct net *net = sock_net(sk);

0111 int smallest_size = -1, smallest_rover;

0112 kuid_t uid = sock_i_uid(sk);

0113

0114 local_bh_disable();

0115 if (!snum) {

0116 int remaining, rover, low, high;

0117

0118 again:

0119 inet_get_local_port_range(&low, &high);

0120 remaining = (high - low) + 1;

0121 smallest_rover = rover = net_random() % remaining + low;

0122

0123 smallest_size = -1;

0124 do {

0125 if (inet_is_reserved_local_port(rover))

0126 goto next_nolock;

...

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

// SLES12 kernel 3.12.69-60.64.32:

0104intinet_csk_get_port(structsock*sk,unsignedshortsnum)

0105{

0106structinet_hashinfo*hashinfo=sk->sk_prot->h.hashinfo;

0107structinet_bind_hashbucket*head;

0108structinet_bind_bucket*tb;

0109intret,attempts=5;

0110structnet*net=sock_net(sk);

0111intsmallest_size=-1,smallest_rover;

0112kuid_tuid=sock_i_uid(sk);

0113

0114local_bh_disable();

0115if(!snum){

0116intremaining,rover,low,high;

0117

0118again:

0119inet_get_local_port_range(&low,&high);

0120remaining=(high-low)+1;

0121smallest_rover=rover=net_random()%remaining+low;

0122

0123smallest_size=-1;

0124do{

0125if(inet_is_reserved_local_port(rover))

0126gotonext_nolock;

...

connect()通过inet_hash_connect()分配端口号。核心的代码是:

port = low + (i + offset) % remaining;

其中 offset 是随机数。

// SLES12 kernel 3.12.69-60.64.32:

0477 int __inet_hash_connect(struct inet_timewait_death_row *death_row,

0478 struct sock *sk, u32 port_offset,

0479 int (*check_established)(struct inet_timewait_death_row *,

0480 struct sock *, __u16, struct inet_timewait_sock **),

0481 int (*hash)(struct sock *sk, struct inet_timewait_sock *twp))

0482 {

0483 struct inet_hashinfo *hinfo = death_row->hashinfo;

0484 const unsigned short snum = inet_sk(sk)->inet_num;

0485 struct inet_bind_hashbucket *head;

0486 struct inet_bind_bucket *tb;

0487 int ret;

0488 struct net *net = sock_net(sk);

0489 int twrefcnt = 1;

0490

0491 if (!snum) {

0492 int i, remaining, low, high, port;

0493 static u32 hint;

0494 u32 offset = hint + port_offset;

0495 struct inet_timewait_sock *tw = NULL;

0496

0497 inet_get_local_port_range(&low, &high);

0498 remaining = (high - low) + 1;

0499

0500 local_bh_disable();

0501 for (i = 1; i <= remaining; i++) {

0502 port = low + (i + offset) % remaining;

0503 if (inet_is_reserved_local_port(port))

0504 continue;

...

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

// SLES12 kernel 3.12.69-60.64.32:

0477int__inet_hash_connect(structinet_timewait_death_row*death_row,

0478structsock*sk,u32port_offset,

0479int(*check_established)(structinet_timewait_death_row*,

0480structsock*,__u16,structinet_timewait_sock**),

0481int(*hash)(structsock*sk,structinet_timewait_sock*twp))

0482{

0483structinet_hashinfo*hinfo=death_row->hashinfo;

0484constunsignedshortsnum=inet_sk(sk)->inet_num;

0485structinet_bind_hashbucket*head;

0486structinet_bind_bucket*tb;

0487intret;

0488structnet*net=sock_net(sk);

0489inttwrefcnt=1;

0490

0491if(!snum){

0492inti,remaining,low,high,port;

0493staticu32hint;

0494u32offset=hint+port_offset;

0495structinet_timewait_sock*tw=NULL;

0496

0497inet_get_local_port_range(&low,&high);

0498remaining=(high-low)+1;

0499

0500local_bh_disable();

0501for(i=1;i<=remaining;i++){

0502port=low+(i+offset)%remaining;

0503if(inet_is_reserved_local_port(port))

0504continue;

...

为以上代码生成随机数port_offset的函数是:

0391 static inline u32 inet_sk_port_offset(const struct sock *sk)

0392 {

0393 const struct inet_sock *inet = inet_sk(sk);

0394 return secure_ipv4_port_ephemeral(inet->inet_rcv_saddr,

0395 inet->inet_daddr,

0396 inet->inet_dport);

0397 }

1

2

3

4

5

6

7

0391staticinlineu32inet_sk_port_offset(conststructsock*sk)

0392{

0393conststructinet_sock*inet=inet_sk(sk);

0394returnsecure_ipv4_port_ephemeral(inet->inet_rcv_saddr,

0395inet->inet_daddr,

0396inet->inet_dport);

0397}

综上,临时端口号是这样产生的:

生成一个随机数,利用随机数在ip_local_port_range范围内取值,如果取到的值在ip_local_reserved_ports范围内 ,那就再依次取下一个值,直到不在ip_local_reserved_ports范围内为止。

注:

/proc/sys/net/ipv4/ip_local_reserved_ports (net.ipv4.ip_local_reserved_ports) 是应用程序保留的端口号,不会参与内核动态分配。有些软件比如SAP通常会保留大量的端口号,如果导致剩下的临时端口数量太少的话,动态分配的随机算法往往会产生重复的端口号,造成新分配的端口号总是相同的现象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值