@@ -86,15 +86,15 @@ func (n *upnp) AddMapping(protocol string, extport, intport int, desc string, li
8686 }
8787 protocol = strings .ToUpper (protocol )
8888 lifetimeS := uint32 (lifetime / time .Second )
89- n .DeleteMapping (protocol , extport , intport )
9089
91- err = n . withRateLimit ( func () error {
92- return n . client . AddPortMapping ( "" , uint16 ( extport ), protocol , uint16 ( intport ), ip . String (), true , desc , lifetimeS )
93- })
94- if err == nil {
95- return uint16 ( extport ), nil
90+ if extport == 0 {
91+ extport = intport
92+ } else {
93+ // Only delete port mapping if the external port was already used by geth.
94+ n . DeleteMapping ( protocol , extport , intport )
9695 }
97- // Try addAnyPortMapping if mapping specified port didn't work.
96+
97+ // Try to add port mapping, preferring the specified external port.
9898 err = n .withRateLimit (func () error {
9999 p , err := n .addAnyPortMapping (protocol , extport , intport , ip , desc , lifetimeS )
100100 if err == nil {
@@ -105,18 +105,28 @@ func (n *upnp) AddMapping(protocol string, extport, intport int, desc string, li
105105 return uint16 (extport ), err
106106}
107107
108+ // addAnyPortMapping tries to add a port mapping with the specified external port.
109+ // If the external port is already in use, it will try to assign another port.
108110func (n * upnp ) addAnyPortMapping (protocol string , extport , intport int , ip net.IP , desc string , lifetimeS uint32 ) (uint16 , error ) {
109111 if client , ok := n .client .(* internetgateway2.WANIPConnection2 ); ok {
110112 return client .AddAnyPortMapping ("" , uint16 (extport ), protocol , uint16 (intport ), ip .String (), true , desc , lifetimeS )
111113 }
112- // It will retry with a random port number if the client does
113- // not support AddAnyPortMapping.
114- extport = n .randomPort ()
114+ // For IGDv1 and v1 services we should first try to add with extport.
115115 err := n .client .AddPortMapping ("" , uint16 (extport ), protocol , uint16 (intport ), ip .String (), true , desc , lifetimeS )
116- if err != nil {
117- return 0 , err
116+ if err == nil {
117+ return uint16 (extport ), nil
118+ }
119+
120+ // If above fails, we retry with a random port.
121+ // We retry several times because of possible port conflicts.
122+ for i := 0 ; i < 3 ; i ++ {
123+ extport = n .randomPort ()
124+ err := n .client .AddPortMapping ("" , uint16 (extport ), protocol , uint16 (intport ), ip .String (), true , desc , lifetimeS )
125+ if err == nil {
126+ return uint16 (extport ), nil
127+ }
118128 }
119- return uint16 ( extport ), nil
129+ return 0 , err
120130}
121131
122132func (n * upnp ) randomPort () int {
0 commit comments