blob: 470f365a1f1304939b2646a1e0380bc3d035e0be [file] [log] [blame]
Zdravko Bozakov7401ff22019-05-28 22:45:12 +02001/*
2 * Copyright 2018-present Open Networking Foundation
3
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7
8 * http://www.apache.org/licenses/LICENSE-2.0
9
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package core
18
19import (
20 "errors"
21 "net"
22 "strconv"
23 "time"
24
Zdravko Bozakov078a2712019-07-19 23:25:15 +020025 api "github.com/opencord/voltha-bbsim/api"
Zack Williams2abf3932019-08-05 14:07:05 -070026 "github.com/opencord/voltha-bbsim/common/logger"
27 "github.com/opencord/voltha-bbsim/device"
Zdravko Bozakov078a2712019-07-19 23:25:15 +020028 "github.com/opencord/voltha-bbsim/flow"
29 openolt "github.com/opencord/voltha-protos/go/openolt"
Zdravko Bozakov7401ff22019-05-28 22:45:12 +020030 "google.golang.org/grpc/codes"
31 "google.golang.org/grpc/status"
32)
33
Zdravko Bozakov078a2712019-07-19 23:25:15 +020034// Constants for reboot delays
35const (
36 OltRebootDelay = 40
37 OnuSoftRebootDelay = 10
38 OnuHardRebootDelay = 30
39)
40
41// handleONUStatusRequest process ONU status request
42func (s *Server) handleONUStatusRequest(in *api.ONUInfo) (*api.ONUs, error) {
43 logger.Trace("handleONUStatusRequest() invoked")
44 onuInfo := &api.ONUs{}
Zdravko Bozakov7401ff22019-05-28 22:45:12 +020045 if in.OnuSerial != "" { // Get status of single ONU by SerialNumber
46 // Get OpenOlt serial number from string
47 sn, err := getOpenoltSerialNumber(in.OnuSerial)
48 if err != nil {
49 logger.Error("Invalid serial number %s", in.OnuSerial)
50 return onuInfo, status.Errorf(codes.InvalidArgument, "serial: "+in.OnuSerial+" is invalid")
51 }
52 // Get ONU by serial number
53 onu, err := getOnuBySN(s.Onumap, sn)
54 if err != nil {
55 logger.Error("ONU with serial number %v not found", sn)
56 return onuInfo, status.Errorf(codes.NotFound, "serial: "+in.OnuSerial+" not found")
57 }
58 onuInfo.Onus = append(onuInfo.Onus, copyONUInfo(onu))
59 } else {
60 // Return error if specified PON port does not exist
61 if _, exist := s.Onumap[in.PonPortId]; !exist {
62 logger.Error("PON port %d not found", in.PonPortId)
63 return onuInfo, status.Errorf(codes.NotFound, "PON Port: "+strconv.Itoa(int(in.PonPortId))+" not found")
64 }
65
66 if in.OnuId != 0 { // Get status of single ONU by ONU-ID
67 for intfid := range s.Onumap {
68 for _, onu := range s.Onumap[intfid] {
69 if in.OnuId == onu.OnuID {
70 onuInfo.Onus = append(onuInfo.Onus, copyONUInfo(onu))
71 }
72 }
73 }
74 } else {
75 // Append ONU data
76 for _, onu := range s.Onumap[in.PonPortId] {
77 onuInfo.Onus = append(onuInfo.Onus, copyONUInfo(onu))
78 }
79 }
80 }
81
82 return onuInfo, nil
83}
84
85// handleONUActivate method handles ONU activate requests from user.
Zdravko Bozakov078a2712019-07-19 23:25:15 +020086func (s *Server) handleONUActivate(in []*api.ONUInfo) (*api.BBSimResponse, error) {
87 logger.Trace("handleONUActivate request received")
Zdravko Bozakov7401ff22019-05-28 22:45:12 +020088 logger.Debug("Received values: %+v\n", in)
89
90 // Check if indication is enabled
91 if s.EnableServer == nil {
92 logger.Error(OLTNotEnabled)
Zdravko Bozakov078a2712019-07-19 23:25:15 +020093 return &api.BBSimResponse{}, status.Errorf(codes.FailedPrecondition, OLTNotEnabled)
Zdravko Bozakov7401ff22019-05-28 22:45:12 +020094 }
95
96 onuaddmap := make(map[uint32][]*device.Onu)
97 var newSerialNums []string
98
99 //Iterate request for each PON port specified
100 for _, onu := range in {
101 intfid := onu.PonPortId
102
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200103 if !s.isPonIntfPresentInOlt(intfid) {
104 return &api.BBSimResponse{}, status.Errorf(codes.OutOfRange, "PON-"+strconv.Itoa(int(intfid))+
105 " not present in OLT-"+strconv.Itoa(int(s.Olt.ID)))
106 }
107
108 if s.Olt.PonIntfs[intfid].AlarmState == device.PonLosRaised {
109 return &api.BBSimResponse{}, status.Errorf(codes.FailedPrecondition, "pon-"+strconv.Itoa(int(intfid))+" is not active")
110 }
111
Zdravko Bozakov7401ff22019-05-28 22:45:12 +0200112 // Get the free ONU object for the intfid
113 Onu, err := s.GetNextFreeOnu(intfid)
114 if err != nil {
115 markONUsFree(onuaddmap)
116 logger.Error("Failed to get free ONU object for intfID %d :%v", intfid, err)
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200117 return &api.BBSimResponse{}, status.Errorf(codes.ResourceExhausted, err.Error())
Zdravko Bozakov7401ff22019-05-28 22:45:12 +0200118 }
119
120 // Check if Serial number is provided by user
121 if onu.OnuSerial != "" {
122 // Get OpenOlt serial number from string
123 sn, err := getOpenoltSerialNumber(onu.OnuSerial)
124 if err != nil {
125 logger.Error("Failed to get OpenOlt serial number %v", err)
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200126 Onu.InternalState = device.OnuFree
Zdravko Bozakov7401ff22019-05-28 22:45:12 +0200127 markONUsFree(onuaddmap)
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200128 return &api.BBSimResponse{}, status.Errorf(codes.InvalidArgument, "serial number: "+onu.OnuSerial+" is invalid")
Zdravko Bozakov7401ff22019-05-28 22:45:12 +0200129 }
130
131 // Check if serial number is not duplicate in requested ONUs
132 for _, sn := range newSerialNums {
133 if onu.OnuSerial == sn {
134 logger.Error("Duplicate serial number found %s", sn)
135 // Mark ONUs free
136 markONUsFree(onuaddmap)
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200137 Onu.InternalState = device.OnuFree
138 return &api.BBSimResponse{}, status.Errorf(codes.InvalidArgument, "duplicate serial number: "+onu.OnuSerial+" provided")
Zdravko Bozakov7401ff22019-05-28 22:45:12 +0200139 }
140 }
141 newSerialNums = append(newSerialNums, onu.OnuSerial)
142
143 // Check if serial number already exist
144 _, exist := s.getOnuFromSNmap(sn)
145 if exist {
146 logger.Error("Provided serial number %v already exist", sn)
147 // Mark ONUs free
148 markONUsFree(onuaddmap)
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200149 Onu.InternalState = device.OnuFree
150 return &api.BBSimResponse{}, status.Errorf(codes.AlreadyExists, "serial number: "+onu.OnuSerial+" already exist")
Zdravko Bozakov7401ff22019-05-28 22:45:12 +0200151 }
152
153 // Store user provided serial number in ONU object
154 Onu.SerialNumber = sn
155 }
156 // Store onu object in map for particular intfid
157 onuaddmap[intfid] = append(onuaddmap[intfid], Onu)
158 }
159
160 if len(onuaddmap) >= 1 {
161 //Pass onumap to activateONU to handle indication to VOLTHA
162 s.activateONUs(*s.EnableServer, onuaddmap)
163 }
164
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200165 return &api.BBSimResponse{StatusMsg: RequestAccepted}, nil
Zdravko Bozakov7401ff22019-05-28 22:45:12 +0200166}
167
168// handleONUDeactivate deactivates ONU described by a single ONUInfo object
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200169func (s *Server) handleONUDeactivate(in *api.ONUInfo) error {
170 logger.Trace("handleONUDeactivate() invoked")
Zdravko Bozakov7401ff22019-05-28 22:45:12 +0200171
172 if s.EnableServer == nil {
173 logger.Error(OLTNotEnabled)
174 return status.Errorf(codes.FailedPrecondition, OLTNotEnabled)
175 }
176
177 if in.OnuSerial != "" {
178 // Get OpenOlt serial number from string
179 serialNumber, err := getOpenoltSerialNumber(in.OnuSerial)
180 if err != nil {
181 logger.Error("Invalid serial number %s", in.OnuSerial)
182 return status.Errorf(codes.InvalidArgument, "serial: "+in.OnuSerial+" is invalid")
183 }
184 // Get ONU by serial number
185 onu, exist := s.getOnuFromSNmap(serialNumber)
186 if !exist {
187 logger.Error("ONU with serial number %s not found", in.OnuSerial)
188 return status.Errorf(codes.NotFound, "serial: "+in.OnuSerial+" not found")
189 }
190
191 if err := s.HandleOnuDeactivate(onu); err != nil {
192 return err
193 }
194 } else {
195 if in.OnuId != 0 { // if provided, delete ONU by ONU ID
196 onu, err := getOnuByID(s.Onumap, in.OnuId, in.PonPortId)
197 if err != nil {
198 return err
199 }
200 if err := s.HandleOnuDeactivate(onu); err != nil {
201 return err
202 }
203 } else { // delete all ONUs on provided port
204 if err := s.DeactivateAllOnuByIntfID(in.PonPortId); err != nil {
205 logger.Error("Failed in handleONUDeactivate: %v", err)
206 return err
207 }
208 }
209 }
210 return nil
211}
212
213func (s *Server) handleOLTReboot() {
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200214 logger.Trace("HandleOLTReboot() invoked")
Zdravko Bozakov7401ff22019-05-28 22:45:12 +0200215 logger.Debug("Sending stop to serverActionCh")
216 s.serverActionCh <- OpenOltStop
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200217
218 // Delete all flows
219 err := flow.DeleteAllFlows()
220 if err != nil {
221 logger.Warn("%v", err)
222 }
223
224 // clear flowMap
225 s.FlowMap = make(map[device.FlowKey]*openolt.Flow)
226
227 // clear flow IDs from ONU objects
228 for intfID := range s.Onumap {
229 for _, onu := range s.Onumap[intfID] {
230 onu.Flows = nil
231 }
232 }
233
234 time.Sleep(OltRebootDelay * time.Second)
Zdravko Bozakov7401ff22019-05-28 22:45:12 +0200235
236 logger.Debug("Sending start to serverActionCh")
237 s.serverActionCh <- OpenOltStart
238 for {
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200239 if s.Olt.GetIntState() == device.OltActive {
Zdravko Bozakov7401ff22019-05-28 22:45:12 +0200240 logger.Info("Info: OLT reactivated")
241 break
242 }
243 time.Sleep(2 * time.Second)
244 }
245 s.sendOnuIndicationsOnOltReboot()
246}
247
248func (s *Server) handleONUHardReboot(onu *device.Onu) {
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200249 logger.Trace("handleONUHardReboot() invoked")
Zdravko Bozakov7401ff22019-05-28 22:45:12 +0200250 _ = sendDyingGaspInd(*s.EnableServer, onu.IntfID, onu.OnuID)
251 device.UpdateOnusOpStatus(onu.IntfID, onu, "down")
252 // send operstat down to voltha
Mahir Gunyel32dfd722019-08-05 16:18:06 +0300253 _ = sendOnuInd(*s.EnableServer, onu, "down", "up")
Zdravko Bozakov7401ff22019-05-28 22:45:12 +0200254 // Give OEH some time to perform cleanup
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200255 time.Sleep(OnuHardRebootDelay * time.Second)
Zdravko Bozakov7401ff22019-05-28 22:45:12 +0200256 s.activateOnu(onu)
257}
258
259func (s *Server) handleONUSoftReboot(IntfID uint32, OnuID uint32) {
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200260 logger.Trace("handleONUSoftReboot() invoked")
Zdravko Bozakov7401ff22019-05-28 22:45:12 +0200261 onu, err := s.GetOnuByID(OnuID, IntfID)
262 if err != nil {
263 logger.Error("No onu found with given OnuID on interface %v", IntfID)
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200264 return
Zdravko Bozakov7401ff22019-05-28 22:45:12 +0200265 }
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200266 OnuAlarmRequest := &api.ONUAlarmRequest{
Zdravko Bozakov7401ff22019-05-28 22:45:12 +0200267 OnuSerial: stringifySerialNumber(onu.SerialNumber),
268 AlarmType: OnuLossOfPloam,
269 Status: "on",
270 }
271 // Raise alarm
272 _, err = s.handleOnuAlarm(OnuAlarmRequest)
273 if err != nil {
274 logger.Error(err.Error())
275 }
276 // Clear alarm
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200277 time.Sleep(OnuSoftRebootDelay * time.Second)
Zdravko Bozakov7401ff22019-05-28 22:45:12 +0200278 OnuAlarmRequest.Status = "off"
279 _, err = s.handleOnuAlarm(OnuAlarmRequest)
280 if err != nil {
281 logger.Error(err.Error())
282 }
283}
284
285// GetNextFreeOnu returns free onu object for specified interface ID
286func (s *Server) GetNextFreeOnu(intfid uint32) (*device.Onu, error) {
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200287 logger.Trace("GetNextFreeOnu() invoked")
Zdravko Bozakov7401ff22019-05-28 22:45:12 +0200288 onus, ok := s.Onumap[intfid]
289 if !ok {
290 return nil, errors.New("interface " + strconv.Itoa(int(intfid)) + " not present in ONU map")
291 }
292 for _, onu := range onus {
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200293 if onu.InternalState == device.OnuFree {
Zdravko Bozakov7401ff22019-05-28 22:45:12 +0200294 // If auto generated serial number is already used by some other ONU,
295 // continue to find for other free object
296 snkey := stringifySerialNumber(onu.SerialNumber)
297 if _, exist := s.SNmap.Load(snkey); exist {
298 continue
299 }
300 // Update Onu Internal State
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200301 onu.InternalState = device.OnuInactive
Zdravko Bozakov7401ff22019-05-28 22:45:12 +0200302 return onu, nil
303 }
304 }
305 return nil, errors.New("no free ONU found for pon port: " + strconv.Itoa(int(intfid)))
306}
307
308// DeactivateAllOnuByIntfID deletes all ONUs for given PON port ID
309func (s *Server) DeactivateAllOnuByIntfID(intfid uint32) error {
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200310 logger.Trace("DeactivateAllOnuByIntfID() invoked")
Zdravko Bozakov7401ff22019-05-28 22:45:12 +0200311 for _, onu := range s.Onumap[intfid] {
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200312 if onu.InternalState == device.OnuFree || onu.InternalState == device.OnuInactive {
Zdravko Bozakov7401ff22019-05-28 22:45:12 +0200313 continue
314 }
315 if err := s.HandleOnuDeactivate(onu); err != nil {
316 return err
317 }
318 }
319 return nil
320}
321
322// HandleOnuDeactivate method handles ONU state changes and sending Indication to voltha
323func (s *Server) HandleOnuDeactivate(onu *device.Onu) error {
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200324 logger.Trace("HandleOnuDeactivate() invoked")
325 logger.Info("Deactivating ONU %d for Intf: %d", onu.OnuID, onu.IntfID)
Zdravko Bozakov7401ff22019-05-28 22:45:12 +0200326
327 // Update ONU internal state to ONU_INACTIVE
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200328 s.updateDevIntState(onu, device.OnuInactive)
Zdravko Bozakov7401ff22019-05-28 22:45:12 +0200329
330 // Update ONU operstate to down
331 onu.OperState = "down"
332
333 // Send DyingGasp Alarm to VOLTHA
334 _ = sendDyingGaspInd(*s.EnableServer, onu.IntfID, onu.OnuID)
Mahir Gunyel32dfd722019-08-05 16:18:06 +0300335 _ = sendOnuInd(*s.EnableServer, onu, onu.OperState, "down")
Zdravko Bozakov7401ff22019-05-28 22:45:12 +0200336 return nil
337}
338
339func markONUsFree(onumap map[uint32][]*device.Onu) {
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200340 logger.Trace("markONUsFree() invoked")
Zdravko Bozakov7401ff22019-05-28 22:45:12 +0200341 for intfid := range onumap {
342 for _, onu := range onumap[intfid] {
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200343 onu.UpdateIntState(device.OnuFree)
Zdravko Bozakov7401ff22019-05-28 22:45:12 +0200344 }
345 }
346}
347
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200348func copyONUInfo(onu *device.Onu) *api.ONUInfo {
349 onuData := &api.ONUInfo{
Zdravko Bozakov7401ff22019-05-28 22:45:12 +0200350 OnuId: onu.OnuID,
351 PonPortId: onu.IntfID,
352 OnuSerial: stringifySerialNumber(onu.SerialNumber),
353 OnuState: device.ONUState[onu.InternalState],
354 OperState: onu.OperState,
355 }
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200356
357 // update gemports
358 for _, gemPorts := range onu.GemPortMap {
359 onuData.Gemports = append(onuData.Gemports, gemPorts...)
360 }
361
362 // fill T-CONT data for ONU
363 if onu.Tconts != nil {
364 onuData.Tconts = &api.Tconts{
365 UniId: onu.Tconts.UniId,
366 PortNo: onu.Tconts.PortNo,
367 Tconts: onu.Tconts.TrafficScheds,
368 }
369 }
370
Zdravko Bozakov7401ff22019-05-28 22:45:12 +0200371 return onuData
372}
373
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200374func (s *Server) fetchPortDetail(intfID uint32, portType string) (*api.PortInfo, error) {
375 logger.Trace("fetchPortDetail() invoked %s-%d", portType, intfID)
Zdravko Bozakov7401ff22019-05-28 22:45:12 +0200376
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200377 portInfo := &api.PortInfo{}
378 var maxOnu, activeOnu uint32
379 var alarmState device.AlarmState
380 var state string
381
382 // Get info for specified port
383 if portType == device.IntfNni && s.isNniIntfPresentInOlt(intfID) {
384 state = s.Olt.NniIntfs[intfID].OperState
385 alarmState = s.Olt.NniIntfs[intfID].AlarmState
386
387 } else if portType == device.IntfPon && s.isPonIntfPresentInOlt(intfID) {
388 maxOnu = uint32(len(s.Onumap[intfID]))
389 activeOnu = s.getNoOfActiveOnuByPortID(intfID)
390 state = s.Olt.PonIntfs[intfID].OperState
391 alarmState = s.Olt.PonIntfs[intfID].AlarmState
392
393 } else {
394 return &api.PortInfo{}, errors.New(portType + "-" + strconv.Itoa(int(intfID)) + " not present in OLT-" +
395 strconv.Itoa(int(s.Olt.ID)))
Zdravko Bozakov7401ff22019-05-28 22:45:12 +0200396 }
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200397
398 // fill proto structure
399 portInfo = &api.PortInfo{
400 PortType: portType,
401 PortId: intfID,
402 PonPortMaxOnus: maxOnu,
403 PonPortActiveOnus: activeOnu,
404 PortState: state,
405 }
406
407 // update alarm state only when alarm is raised
408 if alarmState == device.NniLosRaised || alarmState == device.PonLosRaised {
409 portInfo.AlarmState = device.OLTAlarmStateToString[alarmState]
410 }
411
412 return portInfo, nil
Zdravko Bozakov7401ff22019-05-28 22:45:12 +0200413}
414
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200415func (s *Server) validateDeviceActionRequest(request *api.DeviceAction) (*api.DeviceAction, error) {
416 logger.Trace("validateDeviceActionRequest() invoked")
Zdravko Bozakov7401ff22019-05-28 22:45:12 +0200417 switch request.DeviceType {
418 case DeviceTypeOnu:
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200419 if request.SerialNumber == "" {
Zdravko Bozakov7401ff22019-05-28 22:45:12 +0200420 return request, errors.New("onu serial number can not be blank")
421 }
422
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200423 if len(request.SerialNumber) != SerialNumberLength {
Zdravko Bozakov7401ff22019-05-28 22:45:12 +0200424 return request, errors.New("invalid serial number provided")
425 }
426
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200427 _, exist := s.SNmap.Load(request.SerialNumber)
428 if !exist {
429 return &api.DeviceAction{}, errors.New(request.SerialNumber + " not present in OLT-" +
430 strconv.Itoa(int(s.Olt.ID)))
431 }
432
433 if request.Action != SoftReboot && request.Action != HardReboot {
Zdravko Bozakov7401ff22019-05-28 22:45:12 +0200434 return request, errors.New("invalid device action provided")
435 }
436 return request, nil
437 case DeviceTypeOlt:
438 request.DeviceType = DeviceTypeOlt
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200439 request.Action = HardReboot
Zdravko Bozakov7401ff22019-05-28 22:45:12 +0200440 return request, nil
441 default:
442 return request, errors.New("invalid device type")
443 }
444}
445
446func (s *Server) getNoOfActiveOnuByPortID(portID uint32) uint32 {
447 var noOfActiveOnus uint32
448 for _, onu := range s.Onumap[portID] {
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200449 if onu.InternalState >= device.OnuActive {
Zdravko Bozakov7401ff22019-05-28 22:45:12 +0200450 noOfActiveOnus++
451 }
452 }
453 return noOfActiveOnus
454}
455
456func (s *Server) isPonIntfPresentInOlt(intfID uint32) bool {
457 for _, intf := range s.Olt.PonIntfs {
458 if intf.IntfID == intfID {
459 return true
460 }
461 }
462 return false
463}
464
465func (s *Server) isNniIntfPresentInOlt(intfID uint32) bool {
466 for _, intf := range s.Olt.NniIntfs {
467 if intf.IntfID == intfID {
468 return true
469 }
470 }
471 return false
472}
473
474func getOltIP() net.IP {
475 // TODO make this better
476 conn, err := net.Dial("udp", "8.8.8.8:80")
477 if err != nil {
478 logger.Error(err.Error())
479 return net.IP{}
480 }
481 defer func() {
482 err := conn.Close()
483 if err != nil {
484 logger.Error(err.Error())
485 }
486 }()
487
488 localAddr := conn.LocalAddr().(*net.UDPAddr)
489
490 return localAddr.IP
491}