Showing posts with label network. Show all posts
Showing posts with label network. Show all posts

Tuesday, October 29, 2024

Network location awareness service - enhancements

I've seen several issues related to this service and other services that depend on it in domain environments. From support cases and our own troubleshooting, there are some changes you can make to help make it more reliable as well as improve the reliability of BITS. 

 1)  HKLM\System\CurrentControlSet\Services\NlaSvc\Parameters.  Create dword AlwaysExpectDomainController = 0x1
2)  HKLM\System\CurrentControlSet\Services\DnsCache\Parameters.  Create dword MaxNegativeCacheTTL = 0x0
3)  HKLM\System\CurrentControlSet\Services\Netlogon\Parameters.  Create dword NegativeCachePeriod = 0x0
4) HKLM\System\CurrentControlSet\Services\NlaSvc.  Edit DependOnService reg_multi_sz to ensure that NSI, RPCSs, TcpIp, Dhcp, Eventlog, and Netlogon are in the list.  If you are running a domain controller that is also a DNS server, I would recommend adding Dns to this list.
5)  HKLM\System\CurrentControlSet\Services\bits.  Edit DependOnService reg_multi_sz to add nlasvc to the list.

These keys will help ensure the machine retries network detection failures and does it in an efficient way by turning off negative result caching.  The dependency addition helps ensure the right services are started first to get a domain network detection (especially the netlogon service).  I've noticed that BITS doesn't seem to recheck the network connection profile information that NLAsvc provides, so it can cause problems if bits starts before nlasvc.  Setting that additional dependency will help ensure bits does start throwing TransientError, "There are currently no active network connections" messages on bits jobs.

Wednesday, March 4, 2020

End to end network latency

When it comes to testing connectivity and latency, I've noticed that many IT technicians don't seem to have any tools in their skill set that go beyond a ping. While that works in many situations, there are often situations where ICMP traffic (including ping) is blocked. At this point, connectivity testing skill set often falls down to a telnet command to the port to see if its open, instead of using many already available tools like the powershell test-netconnection cmdlet. Unfortunately, that commandlet and telnet only show that you can connect to a port, and it doesn't tell you how long it takes to get to it. If you're on a windows machine, you can use the Test-PortLatency function that I've written below. This will give a rough idea of the time to connect in milliseconds to a remote tcp port. If you're on a linux machine, the nmap suite of tools has several programs that give latency information, like nping, or just nmap itself. There are other options as well, and typically some programming languages like python or perl available, which should have some capability to create a simple script to provide this information.

While these tools provide latency from one source point to another, you may find that you need to run tests from multiple points. Connections can get complicated with multiple layers and applications that give you a full end to end experience through multiple servers and protocols. In these cases, you will need to work through to determine what your various points in the connection are, and at what points you need to test from. For example, you may be doing remote desktop through a jump server (bastion host). Your workstation doesn't have direct access to the final remote desktop server. Testing connectivity from the jump server to the final destination only gives you part of the total round trip end to end connectivity. You will need to test from the workstation to the jump server, and then add the latency of the jump server to the final destination to get a rough idea of your end to end latency. If you can run ping's at the different layers, it will help give an idea of packet loss as well.

function Test-PortLatency {

              param ( [parameter(mandatory=$true)][string]$Computer,
                              [parameter(mandatory=$true)][int]$Port,
                              [parameter(helpmessage="Timeout in milliseconds")]$timeout=10000
               )

               $starttime = get-date
               $Testconn = New-Object Net.Sockets.TcpClient
               $Testconn.BeginConnect( $computer, $Port, $Null, $Null ) | Out-Null
               $MaxTimeout = ( Get-Date ).AddMilliseconds( $timeout)
               $millisec = 0

               While ( -not $Testconn.Connected -and ( Get-Date ) -lt $maxTimeout )

                   {
                              Sleep -Milliseconds 10
                              $ms += 10          
               }

               $endtime = get-date
               $result = new-object psobject
               add-member -input $result NoteProperty Connected $($testconn.connected)
               add-member -input $result NoteProperty Milliseconds $(($endtime - $starttime).totalmilliseconds)

               if ($testconn.client.remoteendpoint -eq $null) {
                              $resultstr = "Connection_Refused"

               } elseif ($result.milliseconds -gt $timeout) {
                              $resultstr = "Connection_TimedOut"
               } elseif ($result.connected) {
                              $resultstr = "Successful_connection"
               } else {
                              $resultstr = "status_unknown"
               }

               add-member -input $result NoteProperty Result $resultstr
               $Testconn.Close()
               $result

}

Thursday, March 21, 2013

Get-Ipconfig

This script is a really old PS v1 script that I put together for reading network configurations remotely. It uses a mix of WMI and remote registry reading (to get primary dns suffix). It gives roughly the same information that ipconfig /all will give


Example

PS C:\> get-ipconfig localhost

GUID             : {5F09EE0E-A3AD-4C19-BEE1-84E8DFF27462}
HostName         : MYWORKSTATION
NICName          : [00000011] Intel(R) Centrino(R) Ultimate-N 6300 AGN
MacAddress       : 24:77:03:DC:97:38
DHCPEnabled      : True
DHCPServer       : 192.168.0.1
IPAddress        : {192.168.0.108, fe80::31b6:ee7:18b1:2820}
SubnetMask       : {255.255.255.0, 64}
Gateway          : {192.168.0.1}
DNSservers       : {192.168.0.1}
DnsDomain        :
PrimaryDnsSuffix :
DNSSearchList    : {contoso.com, east.contoso.com}
WinsPrimary      :
WinsSecondary    :




$server = $args[0]

if ([string]::IsNullOrEmpty($server)) {
 Write-Host -ForegroundColor "yellow" "Usage:  Get-ipconfig "
 Write-Host -ForegroundColor "yellow" "   Provide the name of the remote computer to get most of the network"
 Write-Host -ForegroundColor "yellow" "   setting information provided by ipconfig /all.  This uses a wmi"
 Write-Host -ForegroundColor "yellow" "   lookup on Win32_NetworkAdapaterConfiguration.  For more precise"
 Write-Host -ForegroundColor "yellow" "   details, you can run a query against that."
 Write-Host -ForegroundColor "yellow" "   The script returns a PSObject, so you can use select-object,"
 Write-Host -ForegroundColor "yellow" "   and format commands to adjust the results as needed."
 write-host
 exit
}

###############
#Wmi query the network adapter configuration settings for all NIC chards that are using TCP/IP
###############
$querystr = "Select SettingID,caption,dnshostname,ipaddress,ipsubnet,dhcpenabled,DHCPServer,DnsDomain,"
$querystr += "Macaddress,Dnsserversearchorder,dnsdomainsuffixsearchorder,winsprimaryserver,winssecondaryserver,"
$querystr += "defaultipgateway From Win32_NetworkAdapterConfiguration Where IPEnabled = True"

$nicsettings = gwmi -query $querystr  -ComputerName $server
 

if ($nicsettings -eq $null) {
 Write-Host -ForegroundColor "red"  "WMI lookup failed"
 return $null
}
########################################
#Get primary dns suffix (from registry)#
########################################
$key = "Software\Policies\Microsoft\System\DNSClient"
$type = [Microsoft.Win32.RegistryHive]::LocalMachine
$regkey = [Microsoft.win32.registrykey]::OpenRemoteBaseKey($type,$server)
$regkey = $regkey.opensubkey($key)
$primarysuffix = $regkey.getvalue("PrimaryDnsSuffix")
########################################

#############
#Build PSobject of results in an array to return
############
$results = New-Object collections.arraylist
foreach ($entry in $nicsettings) {
 $myNic = New-Object PSobject
 Add-Member -InputObject $myNic NoteProperty GUID $entry.SettingID
 Add-Member -InputObject $myNic NoteProperty HostName $entry.dnshostname
 Add-Member -InputObject $myNic Noteproperty NICName $entry.caption
 Add-Member -InputObject $myNic NoteProperty MacAddress $entry.MacAddress
 Add-Member -InputObject $myNic NoteProperty DHCPEnabled $entry.dhcpenabled
 if ($entry.dhcpenabled) {
  Add-Member -InputObject $myNic NoteProperty DHCPServer $entry.Dhcpserver
 }
 Add-Member -InputObject $myNic NoteProperty IPAddress $entry.ipaddress
 Add-Member -InputObject $myNic NoteProperty SubnetMask $entry.ipsubnet
 Add-Member -InputObject $myNic Noteproperty Gateway $entry.defaultipgateway
 Add-Member -InputObject $myNic Noteproperty DNSservers $entry.dnsserversearchorder
 Add-Member -InputObject $myNic Noteproperty DnsDomain $entry.dnsdomain
 Add-Member -InputObject $myNic Noteproperty PrimaryDnsSuffix $primarysuffix
 Add-Member -InputObject $myNic Noteproperty DNSSearchList $entry.dnsdomainsuffixsearchorder
 Add-Member -InputObject $myNic Noteproperty WinsPrimary $entry.winsprimaryserver
 Add-Member -InputObject $myNic Noteproperty WinsSecondary $entry.winssecondaryserver
 $results.add($myNic) > $null
}

return $results

Undefined subnets in Active Directory

It seems like in many environments, there is always a disconnect between the people deploying networks, and ensuring the new subnets are defined in the proper active directory sites. Perhaps there are some IPAM solutions or other neat tricks to come up with for finding these, however we can do some basics with powershell as well. Most Active Directory people will be familiar with the netlogon.log, and may know that there are events logged there for connections from clients that do not map into an AD site. You will also see event log messages in the System log / Netlogon Source / EventID 5807 which state that there were a number of siteless clients connecting and you can look at the netlogon.log for further details. Depending on the level of netlogon debugging that is going on, you may have a lot of noise to deal with in there. But with powershell, some filtering and manipulations, we can strip it all down to source IP's very quickly:

$uniqueIP = get-content c:\windows\debug\netlogon.log | 
? { $_ -cmatch "NO_CLIENT_SITE" } | 
% {$_ -match "\d+\.\d+\.\d+\.\d+"|out-null; $matches[0]} | 
Group-Object | Select Count,Name| Sort Name 

Here we have in the pipeline:
1) Get-content to read the file
2) ? = where. -Cmatch for case sensitive matching of NO_CLIENT_SITE.
3) % = foreach. For each matching line, we look for an IPv4 matching pattern and ignore the true/false result, and display the first matched object
4) Then we group all of these IP addresses as we will have duplications
5) Filter the results to just a count of how many occurrence and which IP is the source
6) Sort by the Name attribute. In this case Name = IP address, so we see our IP's in order which helps us see IP's that might all be in one subnet


If we don't want to get too fancy at this point, we can just visually look through our list and identify possible subnets base on how large our typical subnet blocks are allocated in our environment. We can look at the IP settings of the client remotely via WMI with my get-ipconfig script or another method. Since we may not know the actual location that the new network was deployed, sometimes we can get this from router details. If your organization has telnet open on routers, and puts location details in the banner, this is one useful way of checking. You can look at my telnet script to read these banners. Otherwise, sometimes machine naming conventions or other site specific build details can give away the site location [(nbtstat -a ) or powershell version of this netbios command]. Additionally if you run this type of check frequently, you may end up relooking at Ip's that you already defined subnets for. To get around this you can do some extra filtering in the initial powershell command to read the log. Select with the -last option can reduce your results to the last few lines. Otherwise some date matching could be done. Also you can see for what site the subnet exists in using my powershell script for this.

If you want to try grouping the original results by potential /24 bit subnets:


$uniqueip|select  Name,@{name="mask";expression={$_.name.substring(0,$_.name.lastindexof('.'))}}|group mask |
select @{name="PossibleSubnet";expression={$_.name}},@{name="UniqueIPAddr";expression = {$_.group|select -expand name}}



This will provide a guess at the subnet ID, along with all the related IP's that are in that range.

Wednesday, February 6, 2013

Simple Powershell TCP client for reading telnet banner

For many IT pro's you will find yourself in situations where you get an alert that a system you manage is down.  This can be from a variety of issues, but typically you will ping to see if its really down, and sometimes just based on the ICMP reply types for the ping, you can roughly tell where the problem lies.

Some of these results can roughly be translated as follows

     TTL expired in transit = Routing loop.  Can be due to default routes in use and a link being down
     Destination host not reachable OR Request time out = the subnet is accessible or directly connected, but no arp reply for ethernet to work, or firewall filtering is occuring
     Destination network not reachable = a router doesn't know how to get to that subnet
     Request timed out = router may be trying to forward a packet (but the network is not available), system is on the same subnet and down.

You can follow that your ping with a trace-route to see if the link going to the location that the system is in, is up.  Often, you won't get much information from the trace-route as most hop's won't have reverse dns records.  So, if you have an environment with routers that can be accessed by telnet, and the banner information provides some details on where it is, you can telnet to the last responding hop in your trace to see where the network connection drops.  Since telnet.exe is frequently not available on windows machines these days, yet powershell is, you can leverage the .NET framework to do the work for you (and you won't have to lose output prior to the telnet command and wait for the connection to auto disconnect you).


#requires -version 2.0
param(
    [parameter(mandatory=$true,position=0,helpmessage="Hostname or IP")]
  [string]$hostname
)

if (test-connection $hostname) {
    $conn = new-object system.net.sockets.tcpclient($hostname,23)
    $str = $conn.getstream()
    $buff = new-object system.byte[] 1024
    $enc = new-object System.Text.ASCIIEncoding
    start-sleep -m 200
    $output = ""
    while ($str.DataAvailable -and $output -notmatch "username") {
        $read = $str.read($buff,0,1024)
        $output += $enc.getstring($buff, 0, $read)
        start-sleep -m 300
    }
    $conn.close()
    $output
} else {
    Write-Error "Unable to ping or resolve host"
    exit 1
}

Tuesday, December 18, 2012

POSH What do I need to do to get a ping? (test-connection out of resources)

I frequently write powershell scripts that run against a large number of machines. It has been put out in many places that for best practices, and error handling you should check connectivity to the remote machine first. This make sense. Another best practice typically thrown around is to use existing cmdlets. This of course saves the time of writing your own code, however I noticed with test-connection, it seems to have problems when you're running fast code on a large number of machines. If we use the existing Test-Connection in an IF statement for true/false checks of the machine being accessible or not, you may end up at a point where your machine is unable to process the command due to resource issues.

Error: Test-Connection : Testing connection to computer '10.0.0.1' failed: Error due to lack of resources

This is a problem as one of your attempts to handle errors becomes an error by itself. You can get around this with a few other methods. There is the standard ping.exe command which will return different error codes for status. You could use that and read the return to see if it evaluates successfully. Another way is through .NET's Net.NetworkInformation.Ping class. This can be used quickly and with few options, or if you want to control the ping you can look at Net.NetworkInformation.PingOptions.

As an example, here is an old piece of code I put together in the early Powershell v1 days.


function ping-host([string]$server) {
 #This function will perform a simple, small size 
        #single packet ping of a machine and return 
        #true/false for the result
 if ([string]::IsNullOrEmpty($server) ) {return $false}
 #ping first for reachability check
 $po = New-Object net.NetworkInformation.PingOptions
 $po.set_ttl(64)
 $po.set_dontfragment($true)
 [Byte[]] $pingbytes = (65,72,79,89)
 $ping = new-object Net.NetworkInformation.Ping
 $savedEA = $Erroractionpreference
 $ErrorActionPreference = "silentlycontinue"
 $pingres = $ping.send($server, 1000, $pingbytes, $po)
 if (-not $?) {return $false}
 $ErrorActionPreference = $savedEA
 if ($pingres.status -eq "Success") { return $true } else {return $false}
}

This will send a very small ping packet with a 1 sec timeout, hiding any .NET error messages and returning a true or false for availability.



Wednesday, March 21, 2012

Checking NIC for collisions (speed/duplex mismatch) Windows Powershell

This is an old powershell v1 script that I came up with quite a long time ago. I'm sure it can be modified for easier and more robust usage. Since network card settings in the registry can be difficult to decode between vendors, the best way to look for speed/duplex issues is looking at collisions. Microsoft systems are not very details on networking errors, but they do keep track of this type of problem and it is available WMI. The script below pieces together two WMI classes to get the speed setting and looks for collisions.

Results in PSObject array of all NIC's
Computername        NicName                       LinkSpeed          Collisions
------------        -------                       ---------          ----------
MyComputer          Intel(R) PRO/100...                 100                   0


function get-NicError ([string]$server) {
 #This function uses WMI queries against the target machine to get link speed information and
 #check for any recorded network collissions.  It combines the two queries to print out collisions
 #per adapter with link speed.

 if ([string]::IsNullOrEmpty($server)) {
  write-host -foregroundcolor "yellow"  "Usage: get-NicError servername"
  write-host -foregroundcolor "yellow"  "   Get speed and collision details for network adapters"
  return
 }

 $result = @()

 $a = gwmi -namespace root\wmi -class msndis_ethernetmoretransmitcollisions -computername $server |select-object instancename,ndisethernetmoretransmitcollisions
 $b = gwmi -namespace root\wmi -class msndis_linkspeed -computername $server | select-object instancename,ndislinkspeed 

 if (($a -eq $null) -or ($b -eq $null)) {
  write-error "WMI connection error"
  return 
 }

 foreach ($link in $b) {
  $link.ndislinkspeed = $link.ndislinkspeed / 10000
 }

 foreach ($item in $a) {
   foreach ($seconditem in $b) {
    if ($item.instancename -eq $seconditem.instancename) {
      $myres = New-Object psobject
     Add-Member -InputObject $myres NoteProperty Computername $server
     Add-Member -InputObject $myres Noteproperty NicName $item.instancename
     Add-Member -InputObject $myres Noteproperty LinkSpeed $seconditem.ndislinkspeed
     Add-Member -InputObject $myres Noteproperty Collisions $item.ndisethernetmoretransmitcollisions
     $result += $myres
    }
   }
 }

 return $result
   
}

Friday, April 29, 2011

Finding what AD site an IP address is in.

In environments where there are a large number of subnets defined in Active Directory, sometimes it can be difficult to find what site an IP belongs to. You can always run nltest /dsgetsite on the system, but that requires access to the machine and is not very efficient. There is a good command line tool for this called atsn produced by joeware.net, but I thought it would be nice to have something in powershell for this.

This script takes the IP address of the machine as a mandatory argument (ipv4 only). You can provide either a network mask, or a mask length. If no length is provided, then the system will search all subnets from a /32 downwards. The script will search for any less specific subnets that exist in AD and return the first site name that it finds containing this IP.



#get-site-byIP, ipv4


Param(
[Parameter(Mandatory=$true,HelpMessage="IP Address")][validatepattern('^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$')]$ip,
[Parameter(Mandatory=$false,HelpMessage="Netmask")][validatepattern('^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$')]$netmask,
[Parameter(Mandatory=$false,HelpMessage="Mask length")][validaterange(0,32)][int]$masklength
)



function check-subnetformat([string]$subnet) {

$octetsegments = $subnet.split(".")
#Check each octet from last to first. If an octet does not contain 0, check to see
#if it is valid octet value for subnet masks. Then check to make sure that all preceeding
#octets are 255
$foundmostsignficant = $false
for ($i = 3; $i -ge 0; $i--) {
if ($octetsegments[$i] -ne 0) {
if ($foundmostsignificant -eq $true -and $octetsegments[$i] -ne 255) {
Write-Error "The subnet mask has an invalid value"
return $false
} else {
if ((255,254,252,248,240,224,192,128) -contains $octetsegments[$i]) {
$foundmostsignficant = $true
} else {
Write-Error "The subnet mask has an invalid value"
return $false
}

}
}
}
return $true

}


function get-subnetMask-byLength ([int]$length) {

switch ($length) {
"32" { return "255.255.255.255" }
"31" { return "255.255.255.254" }
"30" { return "255.255.255.252" }
"29" { return "255.255.255.248" }
"28" { return "255.255.255.240" }
"27" { return "255.255.255.224" }
"26" { return "255.255.255.192" }
"25" { return "255.255.255.128" }
"24" { return "255.255.255.0" }
"23" { return "255.255.254.0" }
"22" { return "255.255.252.0" }
"21" { return "255.255.248.0" }
"20" { return "255.255.240.0" }
"19" { return "255.255.224.0" }
"18" { return "255.255.192.0" }
"17" { return "255.255.128.0" }
"16" { return "255.255.0.0" }
"15" { return "255.254.0.0" }
"14" { return "255.252.0.0" }
"13" { return "255.248.0.0" }
"12" { return "255.240.0.0" }
"11" { return "255.224.0.0" }
"10" { return "255.192.0.0" }
"9" { return "255.128.0.0" }
"8" { return "255.0.0.0" }
"7" { return "254.0.0.0"}
"6" { return "252.0.0.0"}
"5" { return "248.0.0.0"}
"4" { return "240.0.0.0"}
"3" { return "224.0.0.0"}
"2" { return "192.0.0.0"}
"1" { return "128.0.0.0"}
"0" { return "0.0.0.0"}

}

}

function get-MaskLength-bySubnet ([string]$subnet) {

switch ($subnet) {
"255.255.255.255" {return 32}
"255.255.255.254" {return 31}
"255.255.255.252" {return 30}
"255.255.255.248" {return 29}
"255.255.255.240" {return 28}
"255.255.255.224" {return 27}
"255.255.255.192" {return 26}
"255.255.255.128" {return 25}
"255.255.255.0" {return 24}
"255.255.254.0" {return 23}
"255.255.252.0" {return 22}
"255.255.248.0" {return 21}
"255.255.240.0" {return 20}
"255.255.224.0" {return 19}
"255.255.192.0" {return 18}
"255.255.128.0" {return 17}
"255.255.0.0" {return 16}
"255.254.0.0" {return 15}
"255.252.0.0" {return 14}
"255.248.0.0" {return 13}
"255.240.0.0" {return 12}
"255.224.0.0" {return 11}
"255.192.0.0" {return 10}
"255.128.0.0" {return 9}
"255.0.0.0" {return 8}
"254.0.0.0" {return 7}
"252.0.0.0" {return 6}
"248.0.0.0" {return 5}
"240.0.0.0" {return 4}
"224.0.0.0" {return 3}
"192.0.0.0" {return 2}
"128.0.0.0" {return 1}
"0.0.0.0" {return 0}

}

}

function get-networkID ([string]$ipaddr, [string]$subnetmask) {
$ipoctets = $ipaddr.split(".")
$subnetoctets = $subnetmask.split(".")
$result = ""

for ($i = 0; $i -lt 4; $i++) {
$result += $ipoctets[$i] -band $subnetoctets[$i]
$result += "."
}
$result = $result.substring(0,$result.length -1)
return $result

}

$startMaskLength = 32

#we can take network masks in both length and full octet format. We need to use both. LDAP searches
#use length, and network ID generation is by full octet format.

if ($netmask -ne $null) {
if (-not(&check-subnetformat $netmask)) {
Write-Error "Subnet provided is not a valid subnet"
exit
} else {
$startmasklength = &get-MaskLength-bySubnet $netmask
}
}



$forest = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()
$mytopleveldomain = $forest.schema.name
$mytopleveldomain = $mytopleveldomain.substring($mytopleveldomain.indexof("DC="))
$mytopleveldomain = "LDAP://cn=subnets,cn=sites,cn=configuration," + $mytopleveldomain
$de = New-Object directoryservices.DirectoryEntry($mytopleveldomain)
$ds = New-Object directoryservices.DirectorySearcher($de)
$ds.propertiestoload.add("cn") > $null
$ds.propertiestoLoad.add("siteobject") > $null


for ($i = $startMaskLength; $i -ge 0; $i--) {
#loop through netmasks from /32 to /0 looking for a subnet match in AD

#Go through all masks from longest to shortest
$mask = &get-subnetMask-byLength $i
$netwID = &get-networkID $ip $mask

#ldap search for the network
$ds.filter = "(&(objectclass=subnet)(objectcategory=subnet)(cn=" + $netwID + "/" + $i + "))"
$fu = $ds.findone()
if ($fu -ne $null) {

#if a match is found, return it since it is the longest length (closest match)
Write-Verbose "Found Subnet in AD at site:"
return $fu.properties.siteobject
}
$fu = $null
}

#if we have arrived at this point, the subnet does not exist in AD

return "Subnet_NOT_Assigned"

Tuesday, January 25, 2011

IPv4 pool exhaustion and IPv6

I've seen some stories popping up recently showing what has been known for a long time, Ipv4 is close to out of allocatable space. You can view some reports at this site. With larger companies (like Yahoo!) and the US government pushing towards IPv6, this will be an important skill to have for IT workers. For those with limited exposure, its time to pick up a book and do some playing around. A while back, I was doing this and ran across Hurricane Electric. They are doing a good job of helping with IPv6 technology, and are giving people a chance to place around with some free services. They have a multi-level free certification program for IPv6, which requires hands on real world IPv6 work. The tunneling provides set up instructions for multiple operating systems (including windows). I have been playing with this in the last few days, setting up v6 tunnels, v6 capable web and mail servers, along with DNS. They do have some free dns capability, but for the tests, you need domain names registered elsewhere. Co.cc is good for this as they give free domain registrations in their space. The only problem is they don't let you create AAAA records on their servers. You can however register a domain, and delegate it to ns1.he.net (where you use HE's freedns system to create these records). Another issue that may come up for some users is the ability to allow this type of traffic through a home connection. IP type 41 needs to be allowed, so your home router needs to be able to do more than just TCP/UDP forwarding. If you can get your router to mirror its public IP to a backend machine, its no issue.


IPv6 Certification Badge for nlinley

Monday, August 9, 2010

Getting the NIC card binding order

I was playing around with some ideas today for identifying what is the primary NIC card of a Windows system, so I could add that to some Dns client setting monitoring I'm working on. The problem of NIC bindings has come up in the past during some troubleshooting on servers that were plugging into trunk ports and had a lot of virtual nic's for different subnets. For those that do no know about binding order, when you are in windows, on the network properties page (for newer OS versions that is "Change adapter settings"). There is a menu item, though it may be hidden to make your life more difficult in new OS's. Try alt-N to pull up the network Advanced menu, and select "Advanced Settings". In this dialog box, you will see a list of connections available and their order. You can move NIC's up and down to set what is the primary NIC for a system. On some Os's this requires reboot to take effect. The binding order is stored in the registry under HKLM\System\CurrentControlSet\Services\TcpIP\Linkage key with a multistring value called Bind. This lists devices by GUID. You can match up the guid values here to the interfaces in other registry locations, or read them through WMI. The reason NIC binding can be important can include, selection of default gateway (windows only supports 1 at a time), and determining what interface traffic originating from the machine uses. In the case of my previously mentioned problems, the problem was DNS lookups were going out the wrong interface, into a secluded subnet to DNS servers that could not reply back due to firewall restrictions. Moving the order will allow you to control what NIC is your source IP. In any case, I though it would be useful to throw together a basic script to list out the binding order of multihomed machines. This script returns an array of PSObjects with the following: BindingOrder, Description, GUID, IP, SubnetMask, Gateway; where description is the text value that shows up in your list of network adapters, and GUID is what is used to reference it in the registry.
#get-binding order
#Check a remote machine to see NIC binding order.

$server = $args[0]

if ($server -eq $null -or $server -match "\?") {
 Write-Host -ForegroundColor "yellow" "Usage:  Get-NicOrder "
 Write-Host -ForegroundColor "yellow" "    Enter the name of a system to connect to.  This script will"
 Write-Host -ForegroundColor "yellow" "    provide the network card binding order of a remote machine."
 exit
}

$key = "System\CurrentControlSet\Services\Tcpip\Linkage"
$type = [Microsoft.Win32.RegistryHive]::LocalMachine
$regkey = [Microsoft.win32.registrykey]::OpenRemoteBaseKey($type,$server)

if (-not $?) {
 Write-Host -ForegroundColor "red"  "Cannot check remote machine, exiting...."
 exit
}

$regkey = $regkey.opensubkey($key)
$ArrBindingGUIDs = $regkey.getvalue("Bind")
 
$ArrNicList = Get-WmiObject -ComputerName $server -Query "select Description,settingid,ipaddress,ipsubnet,defaultipgateway from win32_networkadapterconfiguration"

$ArrResults = New-Object collections.ArrayList

for ($i = 0; $i -lt $ArrBindingGUIDs.length; $i++) {
    if ($arrbindingguids[$i].contains("{") {
  $guid = $ArrBindingGUIDS[$i].substring($ArrBindingGUIDs[$i].indexof('{'))
  foreach ($nic in $ArrNicList) {
   if ($nic.settingid -eq $guid) {
    $result = New-Object psobject
    Add-Member -InputObject $result NoteProperty BindingOrder $i
    Add-Member -InputObject $result NoteProperty Description $nic.Description
    Add-Member -InputObject $result NoteProperty GUID $nic.SettingID
    Add-Member -InputObject $result NoteProperty IP $nic.ipAddress
    Add-Member -InputObject $result NoteProperty SubnetMask $nic.ipsubnet
    Add-Member -InputObject $result NoteProperty Gateway $nic.defaultIpGateway
    $Arrresults.add($result) >$null 
   }
  }
 }
}

Return $arrResults
 

Thursday, March 25, 2010

.NET and NetBIOS name resolution

Recently I was looking at what it would take to throw together some code to chase failed logon events through multiple servers and workstations down to the source machine, source process, and/or source network connection. One of the problems that I encounter in security event logs, the source machines are often IP addresses instead of the system name. When dealing with reverse DNS records in dynamic environments, there are problem subnets in which hosts change IP's often. Reverse DNS allows multiple hosts to record the same record with a maximum per entry limit so high that name resolution is worthless. Even if you have a short scavenging period, your data may not be very accurate, or perhaps the source machine did not register a record. One idea that came to mind was doing a WMI lookup against the remote IP to pull the computer name, but that requires the appropriate level of access for remote WMI (if it is enabled), so it may not be as reliable. Being a sysadmin from the pre-windows 2000 period, I like to use nbtstat against the IP address to see the name of remote windows machines. I tried looking around the various classes of .NET and could find anything for netbios style name resolution, so to save myself the trouble of trying to parse through nbtstat command output strings, and dealing with that slowness (when using multiple NIC's and virtualization NIC's), I decided to roll my own solution. The code below was expanded more later for some additional functionality such as flag parsing. This is the simplified version that returns an array of PSObjects containing the various netbios records. For those not familiar with Netbios, it is the 15 character name format used in windows. The 16th byte of the name is a type value. Type 0x00 will give you the workstation name and domain name (group flag is enabled). The netbios query packet is pretty standard other than perhaps the transaction ID. The replied records are all fixed length and names are padded with 0x20 up to the 15 characters for the names. There is a number of records value that tells how many records were returned, so pulling the results is pretty basic.   (NOTE: the script opens a privileged port, so it requires admin rights on the local machine that you are running it on)


Function convert-netbiosType([byte]$val) {
 #note netbios type codes are usually in decimal, but .net likes to deal with bytes
 #as integers.
    
 $myval = [int]$val
 switch($myval) {
  0 { return "Workstation" }
  1 { return "Messenger service" }
  3 { return "Messenger" }
  6 { return "RAS" }
  32 { return "File Service" }
  27 { return "Domain Master Browser" }
  28 { return "Domain Controller" }
  29 { return "Master Browser" }
  30 { return "Browser election" }
  31 { return "NetDDE" }
  33 { return "RAS Client" }
  34 { return "Exchange MS mail connector" }
  35 { return "Exchange Store" }
  36 { return "Exchange Directory" }
  48 { return "Modem sharing service Server"}
  49 { return "Modem sharing service Client"}
  67 { return "SMS client remote control" }
  68 { return "SMS client remote transfer" }
  135 { return "Exchange MTA" }
  default { return "unk" }
 }
 
}



function get-netbios-name ([string]$ip) {
 #Function:  Get netbios name of the remote machine by IP address provided
 #  result:  Error = $null, positive result is hashtable of names
 if (-not ($ip -match "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}")) {
  write-error "The Ip address provided: $ip  is not a valid IPv4 address format"
  return $null
 }
 #ping first for reachability check
 $po = New-Object net.NetworkInformation.PingOptions
 $po.set_ttl(64)
 $po.set_dontfragment($true)
#alexander ping
 [Byte[]] $pingbytes = (65,72,79,89)
 $ping = new-object Net.NetworkInformation.Ping
 $pingres = $ping.send($ip, 1000, $pingbytes, $po)
    
if ($pingres.status -eq "Success") {
    
#netbios name query  NBTNS
 $port=137
 $ipEP = new-object System.Net.IPEndPoint ([system.net.IPAddress]::parse($ip),$port)
 $udpconn = new-Object System.Net.Sockets.UdpClient
 [byte[]] $sendbytes = (0xf4,0x53,00,00,00,01,00,00,00,00,00,00,0x20,0x43,0x4b,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41 ,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,00,00,0x21,00,01)
 $udpconn.client.receivetimeout=1000
 $bytesSent = $udpconn.Send($sendbytes,50,$ipEP)
 $rcvbytes = $udpconn.Receive([ref]$ipEP)
 if ($? -eq $false -or $rcvbytes.length -lt 63) {
  write-error "System is not responding to netbios traffic on port 137, system is not a windows machine, or other error has occurred."
  return $null
    
 } else {
  [array]$nbnames = $null
  #nbtns query results have a number of returned records field at byte #56 of the returned
  #udp payload.  Read this value to find how many records we have
  $startptr = 56
  $numresults = [int]$rcvbytes[$startptr]
  $startptr++
  $namereclen = 18
  #loop through the number of results and get the names + data
  #  NETBIOS result =  15 byte of name (padded if shorted 0x20)
  #                     1 byte of type
  #                     2 byte of flags
    
    
  for ($i = 0; $i -lt $numresults; $i++) {
   $nbname = new-object PSObject
   $tempname = ""
   #read the 15 byte name and convert to human readable string
   for ($j = 0; $j -lt $namereclen -3; $j++) {
    $tempname += [char]$rcvbytes[$startptr + ($i * $namereclen) + $j]
       
   }
   add-member -input $nbname NoteProperty NetbiosName $tempname
   $rectype = convert-netbiosType $rcvbytes[$startptr + ($i * $namereclen) + 15]
   add-member -input $nbname NoteProperty  RecordType $rectype
   if (($rcvbytes[$startptr + ($i * $namereclen) + 16] -band 128) -eq 128 ) {
  #in the flags field, only the high order byte of the 2 is used
  #the left most bit is the Group name flag which can be used for domain
  #name type identification to differentiate the 0x00 type names
    $groupflag = 1
   } else { $groupflag = 0 }
    add-member -input $nbname NoteProperty IsGroupType $groupflag
    $nbnames += $nbname
   }
   return $nbnames
    
  }
 } else {
  write-error "System not pinging: $ip"
  #prompt for another ip to be inputted?
  return $null
 }
  
}

$ip = args[0]
if ($ip -eq $null -or $ip -eq "") {
  write-host "You need to provide an ip address to check"
  exit
}
get-netbios-name $ip

For those that use nbtstat, you will also know that it returns the MAC address, however not in the best or most efficient way. Since nbtstat tries to use every network adapter on the machine, if you have multiple nics and virtualization nics, then it is slow. The benefit of the powershell method above is that you only send out from one NIC. To get the MAC address, the code can be modified to pull it out of the received packets. MAC address is the last useful 6 bytes of the packet (which may be padded with extra 0's at the end). So you can use this to grab those bytes and format it to a mac address string.

$mac = (0,0,0,0,0,0)
$j = 5
for ($i = $rcvbytes.length - 1; $i -gt 0; $i--) {
   if ($rcvbytes[$i] -ne 0x0) {
      $mac[$j] = $rcvbytes[$i]
      $j--
      if ($j -eq -1) { $i = -1 }
   }
}
$macstring = ""
foreach ($byte in $mac) {
  $macstring += ("{0:X2}" -f $byte) + "-"
}
new-object psobject -property @{
   IP = $ip
   MacAddress = $macstring.trim("-")
}

Wednesday, January 13, 2010

Microsoft Annotated netmon traces for common activity

I was digging through my MS blog subscriptions and found a nice post from the Netmon team about the availability of annotated netmon captures for various Microsoft protocols (link to downloads). I would like to say thank you to all involved in collecting this type of information for everyone. I have done some captures of my own in a Vmware lab before for certain OS activities, and I know what a pain it can be to try to get a clean capture of a specific operation. This kind of information is quite useful for looking at low level operations. It helps to see what is normal and abnormal when you don't necessarily know what normal should look like.

This is a great time saver...so again, thanks.

Tuesday, January 12, 2010

TCP Traceroute in windows

Recently at the office, we ran into a typical problem of TCP ports on a few specific servers blocked by firewall or router ACL. Being the network curious guy that I am, it got me looking back into the use of programs that could traceroute to tcp ports. The problem though is every system available for testing is Windows based. My search for a good solution is still in progress, but I have gone in 5 directions so far.

1) Hping2 for windows. Tried the post xp sp2 version of this on both 2003 server and win 7 enterprise. For 2003 server it worked partially, but I couldn't get any options to run and it didn't seem to do anything other than resolving hostnames. For windows 7, it crashes regardless of any compatibility settings or rights. If there is some trick to getting it working, I didn't find it in brief googling for it. Perhaps there is some missing dependency or extra effort required to get this working, I only used the precompiled version that is in the windows version download.

2) .NET sockets. You can set TTL value on a socket object, but apparently the OS will ignore this when setting up a TCP connection.

From MSDN

"Setting this property on a Transmission Control Protocol (TCP) socket is ignored by the TCP/IP stack if a successful connection has been established using the socket."

From netmon it looks like the 3 way handshake goes by system default TTL and skips whatever you requested.

3) Registry hacking. In HKLM\System\CurrentControlSet\Services\Tcpip\Parameters, you can create a DWORD value called DefaultTTL which can change your OS default TTL. The obvious drawback would be that this is going to break your network connections to other systems while you try the traceroute. And if you are remotely connected to a machine you want to run traceroute from, then its not a viable option as you would probably get kicked off the system. Other cavaets are that in windows 2003 server, the setting does not appear to take effect immediately. In Windows 7 however, it does. So if you are at the console of a Win7 machine (maybe also 2008 srv/vista...didn't try that. 2003 server does not appear to update immediately.), you could have a script to update the reg value, make a connection attempt with any standard method of opening an arbitrary TCP connection that can show the IP that the "TTL expired in transit" result is coming from and incrementally up the DefaultTTL registry until you get this error. But overall, this doesn't seem like a practical approach.

4) Success at last, SCAPY for windows. Installation is pretty simple following the guide (http://trac.secdev.org/scapy/wiki/WindowsInstallationGuide).....BUT to get it working you need 7 different components installed on your system to run it. So if you are running from your workstation or a common source where you can have this set up in advance of the problem, it works great. If the network block you are looking for doesn't effect the machine you have it installed on, then it is much less practical and you have to rely on your firewall/network guys to find the cause of the block.

5) Nemesis. I tried to get a bit simpler than scapy with this one. Initially it failed on dll problems. This was fixed with a major downgrade of winpcap to ver 3.0 (http://www.security-forums.com/viewtopic.php?t=29485). Still on Win7, running the program fails when cmd is under admin privileges with unable to create raw socket error. Checking back on the project site shows nemesis hasn't been updated since 2004, so no surprise there.

So in the future, if I have enough time and dig around with programming some more, maybe there will be some simple script solution out there, but I suspect most avenues have been blocked off by restrictions on raw sockets.