@@ -86,15 +86,15 @@ func (n *upnp) AddMapping(protocol string, extport, intport int, desc string, li
86
86
}
87
87
protocol = strings .ToUpper (protocol )
88
88
lifetimeS := uint32 (lifetime / time .Second )
89
- n .DeleteMapping (protocol , extport , intport )
90
89
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 )
96
95
}
97
- // Try addAnyPortMapping if mapping specified port didn't work.
96
+
97
+ // Try to add port mapping, preferring the specified external port.
98
98
err = n .withRateLimit (func () error {
99
99
p , err := n .addAnyPortMapping (protocol , extport , intport , ip , desc , lifetimeS )
100
100
if err == nil {
@@ -105,18 +105,28 @@ func (n *upnp) AddMapping(protocol string, extport, intport int, desc string, li
105
105
return uint16 (extport ), err
106
106
}
107
107
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.
108
110
func (n * upnp ) addAnyPortMapping (protocol string , extport , intport int , ip net.IP , desc string , lifetimeS uint32 ) (uint16 , error ) {
109
111
if client , ok := n .client .(* internetgateway2.WANIPConnection2 ); ok {
110
112
return client .AddAnyPortMapping ("" , uint16 (extport ), protocol , uint16 (intport ), ip .String (), true , desc , lifetimeS )
111
113
}
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.
115
115
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
+ }
118
128
}
119
- return uint16 ( extport ), nil
129
+ return 0 , err
120
130
}
121
131
122
132
func (n * upnp ) randomPort () int {
0 commit comments