forked from microsoft/wcnscripts
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmonitorDNS.ps1
More file actions
237 lines (219 loc) · 10.7 KB
/
monitorDNS.ps1
File metadata and controls
237 lines (219 loc) · 10.7 KB
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
Param(
[parameter(Mandatory = $false)] [string] $FileName = "dnsinfo.txt", # Output file name
[parameter(Mandatory = $false)] [int] $WaitTime = 1200, # In seconds
[parameter(Mandatory = $false)] [int] $Interval = 3, # interval (in seconds) to sleep between querying DNS rules
[parameter(Mandatory = $false)] [bool] $VerifyVfpRules = $true # Flag to skip (expensive and detailed) monitoring of VFP rules
)
# Helper functions
function listDnsPolicies() {
$dnsPolicies = ((Get-HnsPolicyList | where { $_.Policies.InternalPort -EQ 53 } | where { $_.Policies.ExternalPort -EQ 53 }) | Select Policies, ID, References)
return $dnsPolicies
}
function dnsPoliciesToString($dnsPolicies) {
$dnsPolicies | ForEach-Object {
$dnsPoliciesString = $dnsPoliciesString + ("`nID: $($_.ID) `nReferences: $($_.References)`nPolicies:$($_.Policies)")
}
return $dnsPoliciesString
}
function dnsCountersToString($dnsRuleNames, $dnsCounters){
$dnsRuleNames | ForEach-Object {
if (-not ($dnsCounters.Keys -contains $_)){
return "Counters not found for $($_)!"
} elseif ($dnsCounters[$_].Count -ne 4){
return "Expected number of counters not found for $($_)!"
} else{
return "Rule: $($_)`nMatched: $($dnsCounters[$_][0]), Dropped:$($dnsCounters[$_][1]), Pending:$($dnsCounters[$_][2]), Dropped unified flows:$($dnsCounters[$_][3])`n"
}
}
}
function getDnsRules($portGuid, $dnsServerIP) {
$lbRulesRaw = cmd /c "vfpctrl /port $portGuid /list-rule /layer LB_DSR /group LB_DSR_IPv4_OUT"
$lbRules = $lbRulesRaw |
Where-Object { $_ -match ' ' } |
ForEach-Object {
$_ -replace ' ', ''
}
# Filter out DNS rule names
$dnsRuleNamesRaw = $lbRules |
Where-Object { $_ -like "RULE:LB_DSR_*_*_$($dnsServerIP)_53_53_6" -or $_ -like "RULE:LB_DSR_*_*_$($dnsServerIP)_53_53_17" }
# Get DNS rules and counters
$dnsRulesRaw = @()
$dnsRuleNames = @()
$dnsRulesToCounters = @{}
$dnsRuleNamesRaw |
ForEach-Object {
# Get DNS rule name
$fields = $_ -split ":"
$dnsRuleName = $fields[1]
$dnsRuleNames += $dnsRuleName
# Query VFP for specific DNS rule and counters
$dnsRuleRaw = cmd /c "vfpctrl /port $portGuid /get-rule-counter /layer LB_DSR /group LB_DSR_IPv4_OUT /rule $($dnsRuleName)"
$dnsRulesRaw += $dnsRuleRaw
# Parse counters from raw DNS rule output
$dnsCounters = @() # Matched, Dropped, Pending, Packets, and Dropped unified flows
# Filter out whitespaces
$dnsRule = $dnsRuleRaw |
Where-Object { $_ -match ' ' } |
ForEach-Object {
$_ -replace ' ', ''
}
# Get Counters
$dnsRule = $dnsRule |
Where-Object { $_ -like "*packets:*" -or $_ -like "*flows:*" }
$dnsRule |
ForEach-Object {
$fields = $_ -split ":"
$dnsCounters += $fields[1]
}
$dnsRulesToCounters[$dnsRuleName] = $dnsCounters
}
return $dnsRuleNames, $dnsRulesRaw, $dnsRulesToCounters
}
function consistentDnsRules($oldDnsRules, $currentDnsRules) {
if (($currentDnsRules.Count -ne 2) -or ($oldDnsRules.Count -ne 2)) {
return $false
}
for ($i = 0; $i -le (2); $i++) {
if ($oldDnsRules[$i] -ne $currentDnsRules[$i]) {
return $false
}
}
return $true
}
function getPortGuidMap() {
$rawPortsOutput = cmd /c "vfpctrl /list-vmswitch-port"
# Some very case specific parsing
$rawPortsOutput = $rawPortsOutput |
Where-Object { $_ -match ' ' } |
ForEach-Object {
$_ -replace ' ', ''
}
# Port names and MACs
$portMacs = $rawPortsOutput |
Where-Object { $_ -like 'Portname:*' -or $_ -like 'MACaddress:*' }
$port = ""
$mac = ""
$macToPortGuids = @{}
$portMacs |
ForEach-Object {
$fields = $_ -split ":"
Switch ($fields[0]) {
"Portname" { $port = $fields[1] }
"MACaddress" { $mac = $fields[1] }
}
$macToPortGuids[$mac] = $port
}
return $macToPortGuids
}
function getDnsRulesAll($endpoints, $dnsServerIP, $verbose = $false) {
$endpointDnsRuleNames = @{}
$endpointDnsRules = @{}
$endpointDnsCounters = @{}
$macToPortGuids = getPortGuidMap
$expectedDnsRuleCount = ($endpoints).Count * 2
$dnsRulesCount = 0
$endpoints |
ForEach-Object {
$dnsRules, $lbRules, $dnsCounters = getDnsRules $macToPortGuids[$_.MACaddress] $dnsServerIP
$endpointDnsRuleNames[$_.IPAddress] = $dnsRules
$endpointDnsRules[$_.IPAddress] = $lbRules
$endpointDnsCounters[$_.IPAddress] = $dnsCounters
$dnsRulesCount = $dnsRulesCount + $dnsRules.Count
if ($verbose) {
Write-Output "Found DNS rules for pod $($_.IPAddress):`n$($dnsRules)" >> $FileName
Write-Output "Found LB rules for pod $($_.IPAddress):`n$($lbRules)" >> $FileName
}
}
if ($dnsRulesCount -ne $expectedDnsRuleCount) {
Write-Output "Unexpected DNS rule count! Expected: $($expectedDnsRuleCount)`nActual: $($dnsRulesCount)." >> $FileName
}
else {
Write-Output "[OK] Found $($dnsRulesCount) DNS rules across all pods." >> $FileName
}
return $endpointDnsRuleNames, $endpointDnsRules, $endpointDnsCounters
}
# Main
Remove-Item $FileName -ErrorAction SilentlyContinue
while ((listDnsPolicies).Count -ne 2) {
Write-Output "Waiting for DNS policies..."
start-sleep 5
}
Write-Output "Monitoring DNS rules in $FileName..."
$oldDnsPolicies = listDnsPolicies
$dnsServerIP = $oldDnsPolicies[0].Policies.VIPs
Write-Output "[OK] Found DNS Server VIP $($dnsServerIP)" >> $FileName
Write-Output "[OK] Starting DNS policy: $(dnsPoliciesToString $oldDnsPolicies)" >> $FileName
if ($VerifyVfpRules) {
# Get starting VFP DNS rules
$endpoints = (get-hnsendpoint | ? IsRemoteEndpoint -ne True) | Select-Object MACaddress, IPAddress
Write-Output "[OK] Querying starting DNS rules..." >> $FileName
$oldEndpointDnsRuleNames, $oldEndpointDnsRules, $oldEndpointDnsCounters = getDnsRulesAll $endpoints $dnsServerIP $true
}
for ($i = 0; $i -le ($WaitTime / $Interval); $i++) {
$timeNow = Get-Date
Write-Output "#====== Iteration : $i . Time : $timeNow " >> $FileName
$iterationHealth = $true
# Verify HNS DNS policies are consistent
$currentDnsPolicies = listDnsPolicies
if (($currentDnsPolicies).Count -ne 2) {
Write-Output "DNS policies not found!`nOld: $(dnsPoliciesToString $oldDnsPolicies).`nNew: $(dnsPoliciesToString $currentDnsPolicies)" >> $FileName
# Skip analyzing the VFP rules until DNS policies are present again.
$iterationHealth = $false
Write-Output "#====== Iteration $i Completed. Health: $iterationHealth." >> $FileName
Start-Sleep -Seconds $Interval
continue
}
elseif ($currentDnsPolicies[0].ID -ne $oldDnsPolicies[0].ID) {
Write-Output "DNS policies have changed!`nOld: $(dnsPoliciesToString $oldDnsPolicies).`nNew: $(dnsPoliciesToString $currentDnsPolicies)" >> $FileName
Write-Output "Updating new DNS policy to $($currentDnsPolicies.ID)..." >> $FileName
$oldDnsPolicies = $currentDnsPolicies
$iterationHealth = $false
}
else {
Write-Output "[OK] DNS policies are consistent. Current: $($currentDnsPolicies.ID)" >> $FileName
}
if ($VerifyVfpRules) {
# Verify DNS VFP rules are consistent across all endpoints
$endpoints = (get-hnsendpoint | ? IsRemoteEndpoint -ne True) | Select-Object MACaddress, IPAddress
$endpointDnsRuleNames, $endpointDnsRules, $endpointDnsCounters = getDnsRulesAll $endpoints $dnsServerIP
# IP address in $endpoints currently exists. If it exists in old table, then pod was always here.
# If it does not exist in old table, then it is a new pod. Need to add it there.
$endpoints |
ForEach-Object {
if (-not ($endpointDnsRuleNames.Keys -contains $_.IPAddress)) {
Write-Output "DNS rules not found for pod $($_.IPAddress)!" >> $FileName
# Skip, DNS rules are not found...
$iterationHealth = $false
continue
}
elseif ( $endpointDnsRuleNames[$_.IPAddress].Count -ne 2 ) {
Write-Output "DNS rules partially missing for pod $($_.IPAddress)!" >> $FileName
$iterationHealth = $false
}
elseif (-not ($oldEndpointDnsRuleNames.Keys -contains $_.IPAddress)) {
# New pod
Write-Output "Found new pod with IP $($_.IPAddress)." >> $FileName
Write-Output "Found DNS rules for pod $($_.IPAddress):`n$($endpointDnsRuleNames[$_.IPAddress])" >> $FileName
Write-Output "Found LB rules for pod $($_.IPAddress):`n$($endpointDnsRules[$_.IPAddress])" >> $FileName
$oldEndpointDnsRuleNames[$_.IPAddress] = $endpointDnsRuleNames[$_.IPAddress]
$oldEndpointDnsRules[$_.IPAddress] = $endpointDnsRules[$_.IPAddress]
$oldEndpointDnsCounters[$_.IPAddress] = $endpointDnsCounters[$_.IPAddress]
}
# If current != old, then something has changed. Print the rules.
if ((consistentDnsRules $oldEndpointDnsRuleNames[$_.IPAddress] $endpointDnsRuleNames[$_.IPAddress])) {
Write-Output "[OK] DNS rules are consistent for pod $($_.IPAddress)." >> $FileName
Write-Output "DNS rule counters for pod $($_.IPAddress):`n$(dnsCountersToString $endpointDnsRuleNames[$_.IPAddress] $endpointDnsCounters[$_.IPAddress])" >> $FileName
}
else {
Write-Output "DNS rules have changed for pod $($_.IPAddress)!.`nOld:`n$($oldEndpointDnsRules[$_.IPAddress])`nNew:`n$($endpointDnsRules[$_.IPAddress])" >> $FileName
Write-Output "Updating DNS rules & counters for pod $($_.IPAddress) to: $($endpointDnsRuleNames[$_.IPAddress])..." >> $FileName
$iterationHealth = $false
$oldEndpointDnsRuleNames[$_.IPAddress] = $endpointDnsRuleNames[$_.IPAddress]
$oldEndpointDnsRules[$_.IPAddress] = $endpointDnsRules[$_.IPAddress]
$oldEndpointDnsCounters[$_.IPAddress] = $endpointDnsCounters[$_.IPAddress]
}
}
}
Write-Output "Iteration $i Completed. Health: $iterationHealth." >> $FileName
Start-Sleep -Seconds $Interval
}