I'm working with tcl script under ubuntu 12.04, and I'm facing some problem when I try to configure a multicast socket. What I'm trying to do is to forward traffic from some socket to a multicast one, but I don't know why although the multicast socket is created well,apparently; it isn't bound to the multicast group I want to.
This is the script I'm using
#!/bin/sh
# test.tcl \
exec tclsh "$0" ${1+"$#"}
package require udp
set multicastPort "50003"
proc connector {unicastIP multicastIP port {protocol tcp}} {
if { [string equal $protocol "tcp"] } {
socket -server serverTCP -myaddr $unicastIP $port
puts "tcp"
} elseif {[string equal $protocol "udp" ] } {
serverUDP $unicastIP $multicastIP $port
puts "udp"
}
}
proc serverUDP {unicastIP multicastIP port} {
global multicastPort
set socketUDP [udp_open $port]
puts " $unicastIP"
fconfigure $socketUDP -blocking false -translation binary -buffering none -remote [list $unicastIP $port]
#fileevent $socketUDP readable [list gettingData $socketUDP]
set multicastSocket [udp_open $multicastPort]
udp_conf $multicastSocket -ttl 4
fconfigure $multicastSocket -blocking false -translation binary -buffering none -mcastadd $multicastIP -remote [list $multicastIP $port]
fileevent $socketUDP readable [list forwarding $socketUDP $multicastSocket ]
#puts $socketUDP "hello!"
#flush $socketUDP
}
proc forwarding {socketSrc socketDst} {
set data [read -nonewline $socketSrc]
puts "Read data-> $data"
puts -nonewline $socketDst $data
puts "Written data-> [read -nonewline $socketDst]"
}
connector 127.0.0.1 224.0.1.1 50000 udp
vwait forever
However if I run the script and check out the ports in my system, the multicast port is not assigned the proper multicast IP as you can see
~$ netstat -ptnlu
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
udp 0 0 0.0.0.0:50000 0.0.0.0:* 3334/tclsh
udp 0 0 0.0.0.0:50003 0.0.0.0:* 3334/tclsh
Could anyone tell me the reason?
THanks in advance,
Regards!
AFAIK, that is OK. I have a multicast daemon in production using Tcl and its udp package, and netstat and ss tools also show me the socket as listening on the wildcard address.
"The trick" here, I suppose, is that multicasting is one level up the stack: joining a multicast group is not merely opening a socket or an endpoint on the group address but rather sending a very real IGMP "join" message to the local transport segment (Ethernet, in most deployments) and further communicating with the nearby IGMP routers (again, on Ethernet, they're mostly switches).
So, in your case, just fire up tcpdump and see what it dumps when you start your program. A useful call to tcpdump looks something like this:
tcpdump -i eth0 -n 'igmp and host 224.0.1.1'
To observe UDP traffic exchanges use
tcpdump -i eth0 -n 'udp and host 224.0.1.1 and port 50000'
Related
Is it possible to just listen(not create a new connection) for TCP packets on a port which is already in use, i.e. is sending over data from a router to a server.
I am aware that the following starts the listening process on the mentioned port, and saves it in the pcap file:
SERVER SIDE: nc -l -p <port> > file_name.pcap
CLIENT SIDE: sudo tcpdump -s 0 -U -n -i eth0 not host <server_ip> -w file_name.pcap | nc <server_ip> <port>
But this creates a new connection on the given port and captures packet related to it. I want to capture packets on a port which is already being used to send packets.
Netcat doesn't seem to have that capability currently (according to the man page).
When listening netcat typically opens a socket of family AF_INET (network layer, i.e., TCP/UDP) and type SOCK_STREAM (two-way connection). In contrast, to dump packets, tcpdump opens a socket of family AF_PACKET (device driver layer) and type SOCK_RAW (direct access to packets received).
You can observe this with strace to trace syscalls (here, socket and the subsequent bind) and their arguments:
$ sudo strace -e trace=socket,bind nc -l 8888
socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = 3
bind(3, {sa_family=AF_INET, sin_port=htons(8888), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
$
$ sudo strace -e trace=socket,bind tcpdump -w tmp.pcap
[...]
socket(PF_PACKET, SOCK_RAW, 768) = 3
bind(3, {sa_family=AF_PACKET, proto=0x03, if2, pkttype=PACKET_HOST, addr(0)={0, }, 20) = 0
[...]
You can dump traffic at the device driver level (like tcpdump) or the network layer by using a socket of type SOCK_RAW. I.e., you could very well retrieve the file sent over netcat by opening a socket of family AF_INET and type SOCK_RAW, as is implemented in this blog post.
Can I detect whether a client application uses an UDP-socket or a connected UDP-socket?
If yes, how? If no, why?
As I said in my comment above, code call connect on a UDP socket. That enforces only traffic to/from the connection address is allowed (and all other packets get dropped) and allows you to use send instead of sendto, but the traffic is still UDP.
But you can use the netstat command from the command line to see if the datagram socket has a remote address association:
For example, imagine if the code did this:
// create a datagram socket that listens on port 12345
sock = socket(AF_INET, SOCK_DGRAM, 0);
port = 12345;
addrLocal.sin_family = AF_INET;
addrLocal.sin_port = htons(port);
result = bind(sock, (sockaddr*)&addrLocal, sizeof(addrLocal));
// associate the socket only with packets arriving from 1.2.3.4:6666
addrRemote.sin_family = AF_INET;
addrRemote.sin_port = htons(6666);
addrRemote.sin_addr.s_addr = ipaddress; // e.g. "1.2.3.4"
result = connect(sock, (sockaddr*)&addrRemote, sizeof(addrRemote));
A corresponding netstat -a -u will reveal the following:
ubuntu#ip-10-0-0-15:~$ netstat -u -a
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
udp 0 0 ip-10-0-0-15:12345 1.2.3.4:6666 ESTABLISHED
The presence of a value that isn't *:* in the Foreign Address column for the UDP socket will reveal if the socket has connection address associated with it.
I use socat 1.7.3.1-r0 and run following command on an alpine 3.3 linux server:
socat -d -d -d PTY,link=/dev/ttyFOOBAR,echo=0,raw,unlink-close=0 TCP-LISTEN:7000,forever,reuseaddr
Socat will listen for clients and create a bidirectional communication by passing data from the virtual serial port /dev/ttyFOOBAR to the client and back again over TCP. Once the client disconnects socat should exit.
When such a connection is established socat logs the following:
I socat by Gerhard Rieger - see www.dest-unreach.org
I This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit. (http://www.openssl.org/)
I This product includes software written by Tim Hudson (tjh#cryptsoft.com)
I setting option "symbolic-link" to "/dev/ttyFOOBAR"
I setting option "echo" to 0
I setting option "raw"
I setting option "unlink-close" to 0
I openpty({5}, {6}, {"/dev/pts/3"},,) -> 0
N PTY is /dev/pts/3
I setting option "forever" to 1
I setting option "so-reuseaddr" to 1
I socket(2, 1, 6) -> 7
I starting accept loop
N listening on AF=2 0.0.0.0:7000
I accept(7, {2, AF=2 CLIENT_IP:PORT}, 16) -> 8
N accepting connection from AF=2 CLIENT_IP:PORT on AF=2 172.20.0.2:7000
I permitting connection from AF=2 CLIENT_IP:PORT
I close(7)
I resolved and opened all sock addresses
N starting data transfer loop with FDs [5,5] and [8,8]
ss command on the server prints:
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port
tcp ESTAB 0 0 172.20.0.2:7000 CLIENT_IP:PORT
The problem is, that when I disconnect the client (by switching it off), the tcp connection is still established and no addition logging is coming from socat. ss still shows the connection as ESTAB. Any ideas why? When I again connect the client following appears in the logs:
W read(8, 0x7fa8f48c4020, 8192): Connection reset by peer
N socket 2 to socket 1 is in error
N socket 2 (fd 8) is at EOF
I poll timed out (no data within 0.500000 seconds)
I close(5)
I shutdown(8, 2)
I shutdown(8, 2): Socket not connected
N exiting with status 0
But why does this happen on connect instead of disconnect?
If there is no data to send or receive on a socket and you cut the underlying connection neither side is aware until it attempts to send data. Normally, that would be application level data, but at the protocol level you can enable TCP keep alives to emulate flowing data whenever there is no real data.
According to the socat manpage you could try something like:
socat -d -d -d PTY,link=/dev/ttyFOOBAR,echo=0,raw,unlink-close=0 TCP-LISTEN:7000,forever,reuseaddr,keepalive,keepidle=10,keepintvl=10,keepcnt=2
(keepalive actually looks like the essential option but it is unclear what the defaults will be for the tuning options if unset.)
I'm currently starting a Tcl socket server like this:
socket -server Server 0
This lets the operating system pick an available port to start listening on. The question is that I don't want it to pick any port between 1025 and 64k, instead want to know if I can specify a range of ports? Something like this:
socket -server Server 40000-41000
And then the operating system would pick an available port between 40000 and 41000 for the server to listen on. Is there a way to do this? I can't find it in the Tcl API, but is there some nice API call way to do it rather than iterating through the port range until finding an available port?
The OS itself doesn't provide an API capable of doing that, and Tcl doesn't wrap one up for you as it is actually a pretty rare requirement. Conventionally, servers either listen on specific ports (so clients can know exactly what service to ask for; e.g., 21 for FTP, 22 for SSH, 25 for SMTP, 80 for HTTP, 161 for SNMP, 443 for HTTPS, 993 for secure IMAP) or the clients have some other way of being told what to ask for and genuinely any port will do (0 is the special “pick a card, any card” address). You can ask a Tcl server socket for what port it is actually using fconfigure:
set portNumber [lindex [fconfigure $socket -sockname] 2]
But to get a server socket on a port in a specific range? No API for that. We have to cook something ourselves:
for {set port 40000} {$port <= 41000} {incr port} {
if {![catch {
set sock [socket -server $yourHandler $port]
}]} then {
break
}
}
# If we failed...
if {![info exist sock]} {
error "No ports free in range 40k-41k"
}
This works because failing to bind the port will make the socket creation fail (neatly, catchably) and you can then try to bind the next port. It will take a while to scan over the port range, but it will work.
It's probably neater to wrap this up in a procedure. And Tcl 8.6's try construct will make the code a little less obscure:
proc portInRange {from to handler} {
for {set p $from} {$p <= $to} {incr p} {
try {
return [socket -server $handler $p]
} on error {} continue
}
error "No ports free in range $from-$to"
}
No, there's no API for that.
Generally servers listen on a specific port so that the clients can find the server. So such an API is not particularly useful.
Best to just write it yourself.
I set up two tun devices. The data that is written to each tun device is forwarded over a UDP socket to the other tun device using a simple loop:
// the tuntap device is created using these flags
ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
[...]
fd_set fd_list;
FD_ZERO(&fd_list);
FD_SET(fd1, &fd_list); // fd1 is the tun device
FD_SET(fd2, &fd_list); // fd2 is the udp socket
int fds[] = {fd1, fd2};
while(select(max(fd1, fd2)+1, &fd_list, NULL, NULL, NULL) > -1) {
for(i = 0; i < 2; ++i)
if(FD_ISSET(fds[i], &fd_list)) {
nread = read(fds[i], buf, sizeof(buf));
assert(nread > 0);
ret = write(fds[(i+1)%2], buf, nread);
if(ret == -1)
perror("write():");
}
}
After setting up the interfaces using
ifconfig tun0 10.0.0.1
ifconfig tun1 10.0.0.2
I send a ping from one device to the other
ping -I tun1 10.0.0.1
I can see that the IPv4 packet is received by the UDP socket for tun0 and this packet is correctly written to tun0. Also watching the traffic on tun0 using wireshark shows that the packet is received by tun0. However, no ping response packet is created.
I thought that might be a special case for ICMP packets but when I'm using
socat -d -d -d - TCP-LISTEN:2000,so-bindtodevice=tun0 &
sleep 1
echo 2 | socat -d -d -d - TCP:10.0.0.1:2000,so-bindtodevice=tun1
again no connection is established. the connect process (2nd socat call) only continues firing TCP-SYN packets and eventually times out. Again, watching the traffic on tun0 using wireshark shows that the TCP-SYN packet is delivered to the tun0 device.
Why is this packet not forwared to the socat TCP-LISTEN process so it can establish the connection??
Looks like this is a routing error.
When I run the program on two different machines, then the packets are routed through the tun0 device on each machine respectively and http://backreference.org/wp-content/uploads/2010/03/simpletun.tar.bz2 works fine. Running the programm on one machine twice does not work!