blob: c20891071e66baa974af74c0e79c905ac2f5cbba [file] [log] [blame]
Naveen Sampath04696f72022-06-13 15:19:14 +05301/*
2* Copyright 2022-present Open Networking Foundation
3* Licensed under the Apache License, Version 2.0 (the "License");
4* you may not use this file except in compliance with the License.
5* You may obtain a copy of the License at
6*
7* http://www.apache.org/licenses/LICENSE-2.0
8*
9* Unless required by applicable law or agreed to in writing, software
10* distributed under the License is distributed on an "AS IS" BASIS,
11* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12* See the License for the specific language governing permissions and
13* limitations under the License.
14 */
15
16package application
17
18import (
19 "sync"
20 "time"
21
22 "github.com/opencord/voltha-lib-go/v7/pkg/log"
23)
24
25const (
26 dhcpTimeout uint8 = 60
27)
28
29// done channel required to gracefully stop dhcp server handler thread
30var done = make(chan bool)
31
32// dhcpServerInfo map having dhcp network as key and dhcp request response transaction as value
33var dhcpServerInfo map[dhcpServerTag]dhcpTransactionInfo
34
35// alarmsRaised is struct having array of dhcp network for which dhcp unreachable alarm raised
36var alarmsRaised alarmsRaisedInfo
37
38// mux is mutex variable used for lock unlock
39var mux sync.Mutex
40
41// StartDhcpServerHandler starts go routine periodically(every second) to verify DHCP server reachability.
42func StartDhcpServerHandler() {
43 // Intialize global dhcp map and ticker as one second
44 dhcpServerInfo = make(map[dhcpServerTag]dhcpTransactionInfo)
45 ticker := time.NewTicker(1 * time.Second)
46
47 // go routine runs checkDhcpTimeout every second and exit if done value is set.
48 go func() {
49 for {
50 select {
51 case <-done:
52 ticker.Stop()
53 return
54 case <-ticker.C:
55 mux.Lock()
56 checkDhcpTimeout()
57 mux.Unlock()
58
59 }
60 }
61 }()
62}
63
64// checkDhcpTimeout method called every second to verify dhcp timeout for each DHCP network
65func checkDhcpTimeout() {
66 // logger.Debugw(ctx, "[dhcptimeout] DHCP MAP Info", log.Fields{"Map": dhcpServerInfo})
67 for dsTag, dtInfo := range dhcpServerInfo {
68 dtInfo.decrementTimer()
69 if dtInfo.getTimer() == 0 {
70 logger.Debugw(ctx, "[dhcptimeout]Timer Expired", log.Fields{"ctag": dsTag.cTag, "stag": dsTag.sTag})
71 if dtInfo.getReceivedResponseCount() == 0 && !alarmsRaised.isexist(dsTag) {
72 alarmsRaised.add(dsTag)
73 logger.Infow(ctx, "Alarms Raised", log.Fields{"ctag": dsTag.cTag, "stag": dsTag.sTag})
74 }
75
76 // Reset helps in
77 // case 1: when 2 requests, 1 response received within timeout interval.
78 // case 2: 1 request and no response even after timeout. (Unreachable alarm raised)
79 // In both cases, reset method provides additional timeout to receive response before deleting
80 dtInfo.resetRequestResponseCount(dhcpTimeout)
81
82 // Delete dhcp entry in map and continue to process next entry if pending request set to 0
83 if dtInfo.getPendingRequestCount() == 0 {
84 delete(dhcpServerInfo, dsTag)
85 logger.Debugw(ctx, "[dhcptimeout]DhcpServerTag info removed", log.Fields{"ctag": dsTag.cTag, "stag": dsTag.sTag})
86 // logger.Debugw(ctx, "[dhcptimeout] DHCP MAP Info", log.Fields{"Map": dhcpServerInfo})
87 continue
88 }
89 }
90 // Update decremented timer value and continue loop
91 dhcpServerInfo[dsTag] = dtInfo
92 }
93}
94
95// dhcpRequestReceived called for every DHCP request received from client.
96func dhcpRequestReceived(cTag, sTag uint16, smac string) {
97 var dtInfo dhcpTransactionInfo
98 var valueExist bool
99 dsTag := newDhcpServerTag(cTag, sTag)
100
101 mux.Lock()
102 logger.Debugw(ctx, "dhcpRequestReceived", log.Fields{"ctag": cTag, "stag": sTag, "smac": smac})
103 if dtInfo, valueExist = dhcpServerInfo[dsTag]; !valueExist {
104 dtInfo = newDhcpTransactionInfo(dhcpTimeout, smac)
105 dtInfo.incrementPendingRequestCount()
106 }
107
108 // Source mac received in dhcp request is not same as dtInfo mac then
109 // Its new subscriber request, hence increment pending request count.
110 // If multiple dhcp request received with same mac are ignored.
111 if dtInfo.smac != smac {
112 dtInfo.incrementPendingRequestCount()
113 }
114
115 dhcpServerInfo[dsTag] = dtInfo
116 mux.Unlock()
117}
118
119// dhcpResponseReceived called for every DHCP response received from dhcp server.
120func dhcpResponseReceived(cTag, sTag uint16) {
121 var dtInfo dhcpTransactionInfo
122 var valueExist bool
123 dsTag := newDhcpServerTag(cTag, sTag)
124
125 mux.Lock()
126 logger.Debugw(ctx, "dhcpResponseReceived", log.Fields{"ctag": cTag, "stag": sTag})
127 if dtInfo, valueExist = dhcpServerInfo[dsTag]; !valueExist {
128 logger.Warnw(ctx, "Ignore unknown response", log.Fields{"DhcpResp": dsTag})
129 mux.Unlock()
130 return
131 }
132
133 // If already unreachable alarm raised, clear and remove from array
134 if alarmsRaised.isexist(dsTag) {
135 alarmsRaised.remove(dsTag)
136 logger.Infow(ctx, "Alarm Cleared", log.Fields{"ctag": dsTag.cTag, "stag": dsTag.sTag})
137 }
138
139 // Increments received count and decrement pending count
140 dtInfo.responseReceived()
141 logger.Debugw(ctx, "Updated dtInfo", log.Fields{"pendingReq": dtInfo.pendingRequestCount, "receivedReq": dtInfo.receivedResponseCount})
142
143 if dtInfo.getPendingRequestCount() == 0 {
144 delete(dhcpServerInfo, dsTag)
145 } else {
146 dhcpServerInfo[dsTag] = dtInfo
147 }
148 mux.Unlock()
149}
150
151// StopDhcpServerHandler stops dhcp server handler go routine
152func StopDhcpServerHandler() {
153 done <- true
154}
155
156// dhcpServerTag contains unique dhcp network information
157type dhcpServerTag struct {
158 sTag uint16
159 cTag uint16
160}
161
162func newDhcpServerTag(cTag, sTag uint16) dhcpServerTag {
163 var d dhcpServerTag
164 d.sTag = sTag
165 d.cTag = cTag
166 return d
167}
168
169// dhcpTransactionInfo contains DHCP request response transaction information.
170type dhcpTransactionInfo struct {
171 timer uint8
172 pendingRequestCount uint32
173 receivedResponseCount uint32
174 previousRequestCount uint32
175 smac string
176}
177
178func newDhcpTransactionInfo(timer uint8, smac string) dhcpTransactionInfo {
179 var dt dhcpTransactionInfo
180 dt.timer = timer
181 dt.smac = smac
182 return dt
183}
184
185func (dt *dhcpTransactionInfo) getTimer() uint8 {
186 return dt.timer
187}
188
189func (dt *dhcpTransactionInfo) decrementTimer() uint8 {
190 dt.timer--
191 return dt.timer
192}
193
194func (dt *dhcpTransactionInfo) getPendingRequestCount() uint32 {
195 return dt.pendingRequestCount
196}
197
198func (dt *dhcpTransactionInfo) incrementPendingRequestCount() {
199 dt.pendingRequestCount++
200}
201
202func (dt *dhcpTransactionInfo) getReceivedResponseCount() uint32 {
203 return dt.receivedResponseCount
204}
205
206func (dt *dhcpTransactionInfo) responseReceived() {
207 dt.receivedResponseCount++
208 dt.pendingRequestCount--
209}
210
211func (dt *dhcpTransactionInfo) resetRequestResponseCount(timer uint8) {
212 if dt.pendingRequestCount >= dt.previousRequestCount {
213 dt.pendingRequestCount = dt.pendingRequestCount - dt.previousRequestCount
214 }
215 dt.previousRequestCount = dt.pendingRequestCount
216 dt.receivedResponseCount = 0
217 dt.timer = timer
218}
219
220// alarmsRaisedInfo contains the all networks alarm raised information
221type alarmsRaisedInfo struct {
222 arrayInfo []dhcpServerTag
223}
224
225// add an entry into alarm raised array
226func (a *alarmsRaisedInfo) add(val dhcpServerTag) {
227 a.arrayInfo = append(a.arrayInfo, val)
228}
229
230// isexist check if entry exist in alarm raised array
231func (a *alarmsRaisedInfo) isexist(val dhcpServerTag) bool {
232 for _, srvTag := range a.arrayInfo {
233 if srvTag == val {
234 return true
235 }
236 }
237 return false
238}
239
240// remove deletes given entry from alarm raised array
241func (a *alarmsRaisedInfo) remove(val dhcpServerTag) {
242 for ind := range a.arrayInfo {
243 if a.arrayInfo[ind] == val {
244 a.arrayInfo = append(a.arrayInfo[:ind], a.arrayInfo[ind+1:]...)
245 break
246 }
247 }
248}