I have a SIP application that needs to send UDP packets to set up the SIP calls. SIP has a timeout mechanism to cope with delivery failures. An additional thing I would like to be able to do is detect whether a UDP socket is closed in order having to wait the 32s retransmit interval SIP uses.
The cases I am referring to are when an attempt to send to a UDP socket results in an ICMP Destination Unreachable packet being generated by the remote host. If I attempt to send a UDP packet to a host that's up but that the port is not listening I can see the ICMP message arriving back with a packet tracer but the question is how do I get access to that from my C# code?
I'm playing around with raw sockets but as yet have not been able to get the ICMP packets to be received by my program. The sample below never receives a packet even though ICMP messages are arriving on my PC.
Socket icmpListener = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp);
icmpListener.Bind(new IPEndPoint(IPAddress.Any, 0));
byte[] buffer = new byte[4096];
EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
int bytesRead = icmpListener.ReceiveFrom(buffer, ref remoteEndPoint);
logger.Debug("ICMPListener received " + bytesRead + " from " + remoteEndPoint.ToString());
Below is a wireshark trace showing the ICMP responses coming into my PC from an attempt to send a UDP packet from 10.0.0.100 (my PC) to 10.0.0.138 (my router) on a port I know it's not listening on. My problem is how to make use of those ICMP packets to realise the UDP send has failed rather than just waiting for the application to timeout after an arbitrary period?
Nearly 3 years later and I stumbled across http://www.codeproject.com/Articles/17031/A-Network-Sniffer-in-C which gave me enough of a hint to help me find a solution to receiving ICMP packets on Windows 7 (don't know about Vista, which the original question was about but I suspect this solution would work).
The two key points are that the socket has to be bound to a single specific IP address rather than IPAddress.Any and the IOControl call which sets the SIO_RCVALL flag.
Socket icmpListener = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp);
icmpListener.Bind(new IPEndPoint(IPAddress.Parse("10.1.1.2"), 0));
icmpListener.IOControl(IOControlCode.ReceiveAll, new byte[] { 1, 0, 0, 0 }, new byte[] { 1, 0, 0, 0 });
byte[] buffer = new byte[4096];
EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
int bytesRead = icmpListener.ReceiveFrom(buffer, ref remoteEndPoint);
Console.WriteLine("ICMPListener received " + bytesRead + " from " + remoteEndPoint);
Console.ReadLine();
I also had to set a firewall rule to allow ICMP Port Unreachable packets to be received.
netsh advfirewall firewall add rule name="All ICMP v4" dir=in action=allow protocol=icmpv4:any,any
UPDATE: I think I'm going crazy.... That piece of code that you posted is also working for me...
The following piece of code works fine for me(xp sp3):
using System;
using System.Net;
using System.Net.Sockets;
namespace icmp_capture
{
class Program
{
static void Main(string[] args)
{
IPEndPoint ipMyEndPoint = new IPEndPoint(IPAddress.Any, 0);
EndPoint myEndPoint = (ipMyEndPoint);
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp);
socket.Bind(myEndPoint);
while (true)
{
/*
//SEND SOME BS (you will get a nice infinite loop if you uncomment this)
var udpClient = new UdpClient("192.168.2.199", 666); //**host must exist if it's in the same subnet (if not routed)**
Byte[] messagebyte = Encoding.Default.GetBytes("hi".ToCharArray());
int s = udpClient.Send(messagebyte, messagebyte.Length);
*/
Byte[] ReceiveBuffer = new Byte[256];
var nBytes = socket.ReceiveFrom(ReceiveBuffer, 256, 0, ref myEndPoint);
if (ReceiveBuffer[20] == 3)// ICMP type = Delivery failed
{
Console.WriteLine("Delivery failed");
Console.WriteLine("Returned by: " + myEndPoint.ToString());
Console.WriteLine("Destination: " + ReceiveBuffer[44] + "." + ReceiveBuffer[45] + "." + ReceiveBuffer[46] + "." + ReceiveBuffer[47]);
Console.WriteLine("---------------");
}
else {
Console.WriteLine("Some (not delivery failed) ICMP packet ignored");
}
}
}
}
}
Icmp is using an identifier which seems to be different for every icmp "session" (for every icmp socket). So the reply to an icmp packet not sent by the same socket is helpfully filtered out for you. This is why that piece of code won't work. (I'm not sure about this. It's just an assumption after looking at some ICMP traffic.)
You could simply ping the host and see whether you can reach it or not and then try your SIP thing. However that won't work if the other host is filtering out icmp.
An ugly (but working) solution is using winpcap. (Having this as the only working solutions just seems to be too bad to be true.)
What I mean by using winpcap is the you could capture ICMP traffic and then see if the captured packet is about your UDP packet being undeliverable or not.
Here is an example for capturing tcp packets:
http://www.tamirgal.com/home/SourceView.aspx?Item=SharpPcap&File=Example6.DumpTCP.cs
(It shouldn't be too hard to do the same with ICMP.)
Just use connected udp sockets and the OS will match the icmp unreachable and return an error in the udp socket.
Google for connected udp sockets.
So you want to pick up the dest unreachable return icmp packet programmatically? A tough one. I'd say the network stack soaks that up before you can get anywhere near it.
I don't think a pure C# approach will work here. You'll need to use a driver level intercept to get a hook in. Take a look at this app that uses windows' ipfiltdrv.sys to trap packets (icmp,tcp,udp etc) and read/play with them with managed code (c#).
http://www.codeproject.com/KB/IP/firewall_sniffer.aspx?display=Print
Oisin
There are a number of posts on the web mentioning the problem of ICMP Port Unreachable packets no longer being accessible on Vista.
http://www.eggheadcafe.com/software/aspnet/31961998/icmp-port-unreachable-and.aspx
http://social.msdn.microsoft.com/Forums/en-US/Offtopic/thread/5bd8b275-cc6f-43cd-949d-7c411973b2f3/
The stack should give you back an exception when it receives the ICMP. But it doesn't, at least on Vista. And hence you are trying a workaround.
I don't like answers that say it's not possible, but it seems that way. So I suggest you go back a step to the original problem, which was long timeouts in SIP.
You could let the user configure the
timeout (hence sort of complying with
the spec).
You can start doing other things (like checking other proxies) before
the timeout ends.
You could cache known bad destinations (but that would need
good management of the cache.
If icmp, and udp don't give proper error messages, try tcp or another protocol. Just to elicit the desired information.
(Anything is possible, it just may take a lot of resources.)
I am writing this as a separate answer, since the details are completely different from the one I wrote earlier.
So based on the comment from Kalmi about the session ID, it got me to thinking about why I can open up two ping programs on the same machine, and the responses don't cross over. They are both ICMP, therefore both using port-less raw sockets. That means something in the IP stack, has to know what socket those responses were intended for. For ping it turns out there is an ID used in the data of the ICMP package as part of ECHO REQUEST and ECHO REPLY.
Then I ran across this comment on wikipedia about ICMP:
Although ICMP messages are contained
within standard IP datagrams, ICMP
messages are usually processed as a
special case, distinguished from
normal IP processing, rather than
processed as a normal sub-protocol of
IP. In many cases, it is necessary to
inspect the contents of the ICMP
message and deliver the appropriate
error message to the application that
generated the original IP packet, the
one that prompted the sending of the
ICMP message.
Which was elaborated on (indirectly) here:
The internet header plus the first 64
bits of the original datagram's data.
This data is used by the host to match
the message to the appropriate
process. If a higher level protocol
uses port numbers, they are assumed to
be in the first 64 data bits of the
original datagram's data.
Since you are using UDP, which uses ports, it is possible the network stack is routing the ICMP message back to the original socket. This is why your new, and separate, socket is never receiving those messages. I imagine UDP eats the ICMP message.
If I am correct, one solution to this is to open a raw socket and manually create your UDP packets, listen for the anything coming back, and handle UDP and ICMP messages as appropriate. I am not sure what that would look like in code, but I don't imagine it would be too difficult, and may be considered more "elegant" than the winpcap solution.
Additionally this link, http://www.networksorcery.com/enp/default1003.htm, appears to be a great resource for low level network protocols.
I hope this helps.
Related
I am trying to do discovery via UDP, my code sends a multicast message and other devices on the network reply back to me. I am using UdpClient on .NET 4.5.2, binding it on a random port and sending my message to a multicast address that the devices are listening on (e.g. 233.255.255.237:8003). The devices reply to me from the multicast port 8003, but some reply from their own ip (e.g. 10.0.23.66) and some from local broadcast ip (e.g. 10.0.23.255).
This runs just fine on Windows, I can see replies from all devices, but when running on Mono (version 5.2.0.224), I can only see the messages sent from the devices that don't use the local broadcast ip.
When I do tcpdump and open it in Wireshark I can clearly see the UDP messages reaching me, even those with Source = 10.0.23.255 they have the correct destination ip and port(my random port), but the code never picks them up...
I have searched SO and the web and tried literally everything over the past 2 days, different UdpClient constructors, 2 UdpClients on the same port(otherwise no replies are be picked up, receiving code has to listen on the same port as sending code is using) - one for sending and one for receiving, using a plain socket for receiving, setting EnableBroadcast = true, binding to a specific port(multicast port and other), using JoinMulticastGroup(shouldn't matter though, I am only sending messages to multicast address, not receiving the, the replies come directly to my local andpoint), and then some, but nothing works and I'm at the end of my wits...
Maybe there is a mono bug / different behavior, or some arcane setting that can be used, I would appreciate any help.
Here is how my code looks like(omitting parts like cleanup when disposing, etc.):
client = new UdpClient { MulticastLoopback = false, EnableBroadcast = true };
client.Client.Bind(new IPEndPoint(IPAddress.Any, 0));
client.BeginReceive(EndReceive, null);
private void EndReceive(IAsyncResult ar)
{
try
{
var source = new IPEndPoint(IPAddress.Any, 0);
var data = client.EndReceive(ar, ref source);
Console.WriteLine("{0} received msg:\r\n{1}", source.Address, Encoding.UTF8.GetString(data));
}
catch (Exception e)
{
Console.WriteLine(e);
}
client.BeginReceive(EndReceive, null);
}
For sending the multicast message I use client.Send() in try catch as well, the messages are definitely sent and clients are responding as seen on Wireshark, just under Windows I get all the responses written out to Console, under Linux/Mono only the ones that respond from Source=10.0.23.XXX and and the ones from 10.0.23.255 seem to be filtered out (I compared the messages carefully in Wireshark, they are the same whether my code runs on Win or Linux/Mono).
Recently I completed my network sniffer for the company that I am currently working with.
I want to know:
Is there any way to edit the captured packets before reaching the destination? (UDP/TCP)
Is there any other procedure to capture packets?
Here is my code template/procedure:
1. At first, when loading I get all the devices/hostnames on the computer.
IPHostEntry HostEntry = Dns.GetHostEntry((Dns.GetHostName()));
2. For sniffing the socket to capture the packets has to be a raw socket, with the address family being of type internetwork, and protocol being IP.
mainSocket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.IP);
3. Bind the socket to the selected IP address.
mainSocket.Bind(new IPEndPoint(IPAddress.Parse(TEXT-OF-YOUR-HOST-ENTRY), 0));
4. Set the socket options.
5. Capture outgoing packets to byte[] array.
mainSocket.IOControl(IOControlCode.ReceiveAll, YOUR-TRUE-BYTES, YOUR-OUTGOING-BYTES);
6. Start receiving the packets asynchronously.
mainSocket.BeginReceive(BYTE-DATA, 0, BYTE-DATA-LENGTH, SocketFlags.None, new AsyncCallback(YOUR-RECEIVE-FUNCTION), null);
7. Analyze/Parse the bytes received. And don't forget to call another to BeginReceive so that we continue to receive the incoming packets.
8. Finally for parsing, you can create your own class or using the already invented classes/dlls to study your captured packages.
Thats all I have.
I hope this helps for someone trying to start some network sniffer in C#.
I hope that someone shows me the better ways to do this.
Also is there any chance to edit the packages I captured before they reach their destination?
Thank you for your time.
I know this question has been asked many times. I've read ALL the answers and tried EVRY piece of code I could find. After a few days I'm so desperate that I have to ask you for help.
I have a device and a PC in my home network. The device sends UDP broadcast messages. On my PC I can see those messages in wireshark:
Source Destination Length
192.168.1.102 0.0.0.0 UDP 60 Source port: 9050 Destination port: 0
That means the packets are arriving on my PC. My next step was to create a C# application that receives those packets. As mentioned above I tried every possible solution, but it just won't receive anything.
So I guess there must be something very basic I'm doing wrong.
Can anyone help me out? Thanks!
Just experienced the same issue, and wanted to share what fixed it for me.
Briefly: it seems that Windows Firewall was somehow the cause of this weird behavior, and just disabling the service does not help. You have to explicitly allow incoming UDP packets for specific program (executable) in Windows Firewall inbound rules list.
For full case description, read on.
My network setup is: IP of my (receiving) machine was 192.168.1.2, IP of sending machine was 192.168.1.50, and subnet mask on both machines was 255.255.255.0.
My machine is running Windows 7 x64.
This is the code (roughly) that I used:
Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
IPEndPoint iep = new IPEndPoint(IPAddress.Any, 0);
sock.Bind(iep);
sock.EnableBroadcast = true;
EndPoint ep = (EndPoint)iep;
byte[] buffer = new byte[1000];
sock.ReceiveFrom(buffer, ref ep);
Initially this did not work unless I sent a broadcast packet from that socket before I call ReceiveFrom on it. I.e. adding this line before ReceiveFrom call:
sock.SendTo(someData, new IPEndPoint(IPAddress.Broadcast, somePort))
When I didn't send the broadcast packet first from receiving socket, incoming broadcast packets were not received by it, even though they appeared in Wireshark (destination of packets was 255.255.255.255).
I thought that it looks like firewall is messing with incoming packets (unless some kind of UDP hole is opened first by outgoing packet - even though I haven't heard before that UDP hole punching applies to broadcast packets somehow), so I went to services and disabled Windows firewall service altogether. This changed nothing.
However, after trying everything else, I re-enabled the firewall service, and tried to run the program again. This time, firewall prompt appeared asking me whether I want to allow MyProgram.vshost.exe process (I was debugging in Visual Studio) through firewall, I accepted it, and voila - everything worked! Incoming packets were being received now!
You are OK, they have something wired in the code which causes the problem. (I haven't read the article, just copy pasted)
It always works from the local machine, but from a remote machine it will fail for some reason.
To fix this:
In the Broadcst.cs they broadcast twice. once for the localhost and then for the target IP adress (iep2). simply remove the
sock.SendTo(data, iep1);
and it should work.
No idea why.
I'm trying to send a HTTP GET request with UDP (since the reply from the listening server is irrelevant and I don't want to block the program)
This is the code:
System.Net.Sockets.UdpClient client = new System.Net.Sockets.UdpClient();
client.Connect("www.domainname.com", 80);
string request_header = "GET /ping.php HTTP/1.1\r\nHost: www.domainname.com\r\n\r\n";
byte[] stre = System.Text.Encoding.ASCII.GetBytes(request_header);
client.Send(stre, stre.Length);
System.Net.IPEndPoint RemoteIpEndPoint = new System.Net.IPEndPoint(System.Net.IPAddress.Any, 0);
byte[] receiveBytes = client.Receive(ref RemoteIpEndPoint);
string returnData = System.Text.Encoding.ASCII.GetString(receiveBytes);
client.Close();
First, the request doesn't seem to be receieved at the server, so I'm thinking perhaps something goes wrong when sending it?
Second, the program hangs on client.Receive(ref RemoteIpEndPoint), and just waits there. No data seems to be received.
I have tried to change...
System.Net.IPEndPoint(System.Net.IPAddress.Any, 0);
to...
System.Net.IPEndPoint(System.Net.IPAddress.Any, 80);
...but with no luck.
If you don't want to block the client then use the async socket methods, but use TCP. I doubt you'll find a web server that listens on UDP for HTTP requests.
You might also want to look at WireShark which is a network traffic logging tool; you could have used this to see that your UDP datagram most probably WAS being sent but there was no response was generated by the server.
You could also use netstat on the server to see that it isn't listening on UDP port 80.
When a server listens at TCP port 80 it will never be affected by a UDP frame.
TCP and UDP are different protocols. Both support "port numbers", but these are not related.
You can verify this by reading a response on the UDP socket. You should get an error result. The corresponding error code indicate the problem, hat (usually) nobody is listening at UDP port 80.
I have a SIP application that needs to send UDP packets to set up the SIP calls. SIP has a timeout mechanism to cope with delivery failures. An additional thing I would like to be able to do is detect whether a UDP socket is closed in order having to wait the 32s retransmit interval SIP uses.
The cases I am referring to are when an attempt to send to a UDP socket results in an ICMP Destination Unreachable packet being generated by the remote host. If I attempt to send a UDP packet to a host that's up but that the port is not listening I can see the ICMP message arriving back with a packet tracer but the question is how do I get access to that from my C# code?
I'm playing around with raw sockets but as yet have not been able to get the ICMP packets to be received by my program. The sample below never receives a packet even though ICMP messages are arriving on my PC.
Socket icmpListener = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp);
icmpListener.Bind(new IPEndPoint(IPAddress.Any, 0));
byte[] buffer = new byte[4096];
EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
int bytesRead = icmpListener.ReceiveFrom(buffer, ref remoteEndPoint);
logger.Debug("ICMPListener received " + bytesRead + " from " + remoteEndPoint.ToString());
Below is a wireshark trace showing the ICMP responses coming into my PC from an attempt to send a UDP packet from 10.0.0.100 (my PC) to 10.0.0.138 (my router) on a port I know it's not listening on. My problem is how to make use of those ICMP packets to realise the UDP send has failed rather than just waiting for the application to timeout after an arbitrary period?
Nearly 3 years later and I stumbled across http://www.codeproject.com/Articles/17031/A-Network-Sniffer-in-C which gave me enough of a hint to help me find a solution to receiving ICMP packets on Windows 7 (don't know about Vista, which the original question was about but I suspect this solution would work).
The two key points are that the socket has to be bound to a single specific IP address rather than IPAddress.Any and the IOControl call which sets the SIO_RCVALL flag.
Socket icmpListener = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp);
icmpListener.Bind(new IPEndPoint(IPAddress.Parse("10.1.1.2"), 0));
icmpListener.IOControl(IOControlCode.ReceiveAll, new byte[] { 1, 0, 0, 0 }, new byte[] { 1, 0, 0, 0 });
byte[] buffer = new byte[4096];
EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
int bytesRead = icmpListener.ReceiveFrom(buffer, ref remoteEndPoint);
Console.WriteLine("ICMPListener received " + bytesRead + " from " + remoteEndPoint);
Console.ReadLine();
I also had to set a firewall rule to allow ICMP Port Unreachable packets to be received.
netsh advfirewall firewall add rule name="All ICMP v4" dir=in action=allow protocol=icmpv4:any,any
UPDATE: I think I'm going crazy.... That piece of code that you posted is also working for me...
The following piece of code works fine for me(xp sp3):
using System;
using System.Net;
using System.Net.Sockets;
namespace icmp_capture
{
class Program
{
static void Main(string[] args)
{
IPEndPoint ipMyEndPoint = new IPEndPoint(IPAddress.Any, 0);
EndPoint myEndPoint = (ipMyEndPoint);
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp);
socket.Bind(myEndPoint);
while (true)
{
/*
//SEND SOME BS (you will get a nice infinite loop if you uncomment this)
var udpClient = new UdpClient("192.168.2.199", 666); //**host must exist if it's in the same subnet (if not routed)**
Byte[] messagebyte = Encoding.Default.GetBytes("hi".ToCharArray());
int s = udpClient.Send(messagebyte, messagebyte.Length);
*/
Byte[] ReceiveBuffer = new Byte[256];
var nBytes = socket.ReceiveFrom(ReceiveBuffer, 256, 0, ref myEndPoint);
if (ReceiveBuffer[20] == 3)// ICMP type = Delivery failed
{
Console.WriteLine("Delivery failed");
Console.WriteLine("Returned by: " + myEndPoint.ToString());
Console.WriteLine("Destination: " + ReceiveBuffer[44] + "." + ReceiveBuffer[45] + "." + ReceiveBuffer[46] + "." + ReceiveBuffer[47]);
Console.WriteLine("---------------");
}
else {
Console.WriteLine("Some (not delivery failed) ICMP packet ignored");
}
}
}
}
}
Icmp is using an identifier which seems to be different for every icmp "session" (for every icmp socket). So the reply to an icmp packet not sent by the same socket is helpfully filtered out for you. This is why that piece of code won't work. (I'm not sure about this. It's just an assumption after looking at some ICMP traffic.)
You could simply ping the host and see whether you can reach it or not and then try your SIP thing. However that won't work if the other host is filtering out icmp.
An ugly (but working) solution is using winpcap. (Having this as the only working solutions just seems to be too bad to be true.)
What I mean by using winpcap is the you could capture ICMP traffic and then see if the captured packet is about your UDP packet being undeliverable or not.
Here is an example for capturing tcp packets:
http://www.tamirgal.com/home/SourceView.aspx?Item=SharpPcap&File=Example6.DumpTCP.cs
(It shouldn't be too hard to do the same with ICMP.)
Just use connected udp sockets and the OS will match the icmp unreachable and return an error in the udp socket.
Google for connected udp sockets.
So you want to pick up the dest unreachable return icmp packet programmatically? A tough one. I'd say the network stack soaks that up before you can get anywhere near it.
I don't think a pure C# approach will work here. You'll need to use a driver level intercept to get a hook in. Take a look at this app that uses windows' ipfiltdrv.sys to trap packets (icmp,tcp,udp etc) and read/play with them with managed code (c#).
http://www.codeproject.com/KB/IP/firewall_sniffer.aspx?display=Print
Oisin
There are a number of posts on the web mentioning the problem of ICMP Port Unreachable packets no longer being accessible on Vista.
http://www.eggheadcafe.com/software/aspnet/31961998/icmp-port-unreachable-and.aspx
http://social.msdn.microsoft.com/Forums/en-US/Offtopic/thread/5bd8b275-cc6f-43cd-949d-7c411973b2f3/
The stack should give you back an exception when it receives the ICMP. But it doesn't, at least on Vista. And hence you are trying a workaround.
I don't like answers that say it's not possible, but it seems that way. So I suggest you go back a step to the original problem, which was long timeouts in SIP.
You could let the user configure the
timeout (hence sort of complying with
the spec).
You can start doing other things (like checking other proxies) before
the timeout ends.
You could cache known bad destinations (but that would need
good management of the cache.
If icmp, and udp don't give proper error messages, try tcp or another protocol. Just to elicit the desired information.
(Anything is possible, it just may take a lot of resources.)
I am writing this as a separate answer, since the details are completely different from the one I wrote earlier.
So based on the comment from Kalmi about the session ID, it got me to thinking about why I can open up two ping programs on the same machine, and the responses don't cross over. They are both ICMP, therefore both using port-less raw sockets. That means something in the IP stack, has to know what socket those responses were intended for. For ping it turns out there is an ID used in the data of the ICMP package as part of ECHO REQUEST and ECHO REPLY.
Then I ran across this comment on wikipedia about ICMP:
Although ICMP messages are contained
within standard IP datagrams, ICMP
messages are usually processed as a
special case, distinguished from
normal IP processing, rather than
processed as a normal sub-protocol of
IP. In many cases, it is necessary to
inspect the contents of the ICMP
message and deliver the appropriate
error message to the application that
generated the original IP packet, the
one that prompted the sending of the
ICMP message.
Which was elaborated on (indirectly) here:
The internet header plus the first 64
bits of the original datagram's data.
This data is used by the host to match
the message to the appropriate
process. If a higher level protocol
uses port numbers, they are assumed to
be in the first 64 data bits of the
original datagram's data.
Since you are using UDP, which uses ports, it is possible the network stack is routing the ICMP message back to the original socket. This is why your new, and separate, socket is never receiving those messages. I imagine UDP eats the ICMP message.
If I am correct, one solution to this is to open a raw socket and manually create your UDP packets, listen for the anything coming back, and handle UDP and ICMP messages as appropriate. I am not sure what that would look like in code, but I don't imagine it would be too difficult, and may be considered more "elegant" than the winpcap solution.
Additionally this link, http://www.networksorcery.com/enp/default1003.htm, appears to be a great resource for low level network protocols.
I hope this helps.