Are there a constants in scapy for TCP and UDP? - constants

Are there a constants in scapy for TCP and UDP?
I mean
TCP=6, UDP=17
etc...

a)
looking up the implementation for IP we see that IP.proto is a ByteEnumField("proto", 0, IP_PROTOS),. This means, it takes values from the IP_PROTOS list which just loads your os /etc/protocols/. So you could either parse /etc/protocols yourself or, of scapy is already loaded, access the IP_PROTOS object directly:
>>> IP_PROTOS
</etc/protocols/ pim ip ax_25 esp tcp ah mpls_in_ip rohc ipv6_opts xtp st mobility_header dccp igmp ipv6_route igp ddp etherip wesp xns_idp ipv6_frag vrrp gre ipcomp encap ipv6 iso_tp4 sctp ipencap rsvp hip udp ggp hmp idpr_cmtp hopopt fc skip icmp pup manet isis rdp l2tp ipv6_icmp udplite egp ipip ipv6_nonxt eigrp idrp shim6 rspf ospf vmtp>
>>> IP_PROTOS.tcp
6
>>> IP_PROTOS.udp
17
>>> IP_PROTOS.ip
0
b) An alternative approach would be to read scapys layer binding information directly. This is the information that is added to a layer when you (or scapy core itself) calls bind_layers(lower,upper[,overload_fields]). You can easily read that information as follows:
>>> TCP.overload_fields
{<class 'scapy.layers.inet6.IPv6'>: {'nh': 6}, <class 'scapy.layers.inet.IP'>: {'frag': 0, 'proto': 6}}
Means, in case TCP is a payload to IPv4 (scapy.layers.inet.IP) it will override IP.proto=6.
Here's that same information for UDP
>>> UDP.overload_fields
{<class 'scapy.layers.inet6.IPv6'>: {'nh': 17}, <class 'scapy.layers.inet.IP'>: {'frag': 0, 'proto': 17}}
For reference, here is the bind_layers call for TCP/UDP

TCP and UDP are the initiators of TCP/UDP packets.
For example:
pack = IP(dst="www.google.com") / UDP(dport=80)
pack.show()
Result:
>>> pack = IP(dst="www.google.com") / UDP(dport=80)
>>> pack.show()
###[ IP ]###
version= 4
ihl= None
tos= 0x0
len= None
id= 1
flags=
frag= 0
ttl= 64
proto= udp
chksum= None
src= 'Your local address'
dst= Net('www.google.com')
\options\
###[ UDP ]###
sport= domain
dport= http
len= None
chksum= None
>>>

Related

Galera connection issues over haproxy

In our K8 cluster, we use haproxy app for connecting to Galera cluster.
Our haproxy.cnf file looks like
global
maxconn 2048
external-check
stats socket /var/run/haproxy.sock mode 600 expose-fd listeners level user
user haproxy
group haproxy
defaults
log global
mode tcp
retries 10
timeout client 30000
timeout connect 100500
timeout server 30000
frontend mysql-router-service
bind *:6446
mode tcp
option tcplog
default_backend galera_cluster_backend
# MySQL Cluster BE configuration
backend galera_cluster_backend
mode tcp
option tcpka
option mysql-check user haproxy
balance source
server pitipana-opsdb1 192.168.144.82:3306 check weight 1
server pitipana-opsdb2 192.168.144.83:3306 check weight 1
server pitipana-opsdb3 192.168.144.84:3306 check weight 1
Dockerfile for creating haproxy image
FROM haproxy:2.3
COPY haproxy.cfg /usr/local/etc/haproxy/haproxy.cfg
In my Galera nodes, I get constant warning in /var/log/mysql/error.log
2021-12-20 21:16:47 5942 [Warning] Aborted connection 5942 to db: 'ourdb' user: 'ouruser' host: '192.168.1.2' (Got an error reading communication packets)
2021-12-20 21:16:47 5943 [Warning] Aborted connection 5943 to db: 'ourdb' user: 'ouruser' host: '192.168.1.2' (Got an error reading communication packets)
2021-12-20 21:16:47 5944 [Warning] Aborted connection 5944 to db: 'ourdb' user: 'ouruser' host: '192.168.1.2' (Got an error reading communication packets)
I had increased max_packet_size to 64MB and max_connections to 1000.
When I take a tcpdump from galera node :
Frame 16: 106 bytes on wire (848 bits), 106 bytes captured (848 bits)
Linux cooked capture
Internet Protocol Version 4, Src: 192.168.1.2, Dst: 192.168.10.3
Transmission Control Protocol, Src Port: 62495, Dst Port: 3306, Seq: 1, Ack: 1, Len: 50
Source Port: 62495
Destination Port: 3306
[Stream index: 2]
[TCP Segment Len: 50]
Sequence number: 1 (relative sequence number)
[Next sequence number: 51 (relative sequence number)]
Acknowledgment number: 1 (relative ack number)
0101 .... = Header Length: 20 bytes (5)
Flags: 0x018 (PSH, ACK)
000. .... .... = Reserved: Not set
...0 .... .... = Nonce: Not set
.... 0... .... = Congestion Window Reduced (CWR): Not set
.... .0.. .... = ECN-Echo: Not set
.... ..0. .... = Urgent: Not set
.... ...1 .... = Acknowledgment: Set
.... .... 1... = Push: Set
.... .... .0.. = Reset: Not set
.... .... ..0. = Syn: Not set
.... .... ...0 = Fin: Not set
[TCP Flags: ·······AP···]
Window size value: 507
[Calculated window size: 64896]
[Window size scaling factor: 128]
Checksum: 0x3cec [unverified]
[Checksum Status: Unverified]
Urgent pointer: 0
[SEQ/ACK analysis]
[Timestamps]
TCP payload (50 bytes)
[PDU Size: 45]
[PDU Size: 5]
MySQL Protocol
Packet Length: 41
Packet Number: 1
Request Command SLEEP
Command: SLEEP (0)
Payload: 820000008000012100000000000000000000000000000000...
[Expert Info (Warning/Protocol): Unknown/invalid command code]
[Unknown/invalid command code]
[Severity level: Warning]
[Group: Protocol]
MySQL Protocol
Packet Length: 1
Packet Number: 0
Request Command Quit
Command: Quit (1)
Here 192.168.1.2 is a K8 worker node and 192.168.10.3 is the galera node.
When I connect our applications in K8, we can access to applications, but when we try to edit, we get stuck.
Any suggestion to fix this?

Elixir socket duplicate STX and ETX in response

I have socket server with this method:
#impl true
def handle_call({:tcp, socket, packet}, state) do
Logger.info("Received packet: \x02#{packet}\x03 and send response")
{:reply, {:ok, packet}, state}
end
I wrote script in python that send "\x02Test\x03" to socket:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("127.0.0.1", port))
s.send("\x02Test\x03".encode())
print(s.recv(1024))
But the response printed by python is b'\x02\x02Test\x03\x03'
What does handle_call() have to do with gen_tcp?
gen_tcp can be configured to read from a socket and send() messages to whatever process called :gen_tcp.accept(), the so called controlling process, which can be a gen_server; but a gen_server handles messages sent to its mailbox with handle_info()--not handle_call(). handle_call() handles messages sent by :gen_server.call().
Here's an example:
defmodule TcpServer do
use GenServer
require Logger
def start_link() do
ip = Application.get_env :gen_tcp, :ip, {127,0,0,1}
port = Application.get_env :gen_tcp, :port, 6666
IO.puts "gen_tcp is listening on port: #{port}"
GenServer.start_link(__MODULE__, {ip, port},[])
end
def init({ip, port}) do
{:ok, listen_socket}= :gen_tcp.listen(
port,
[:binary, {:packet,0}, {:active,true}, {:ip,ip}]
)
{:ok, socket } = :gen_tcp.accept listen_socket
{:ok, %{ip: ip, port: port, socket: socket} }
end
def handle_call({:tcp, _socket, packet}, state) do
Logger.info("handle_call(): Received packet: #{inspect packet}")
{:reply, {:ok, packet}, state}
end
def handle_info({:tcp,socket,packet},state) do
Logger.info "handle_info(:tcp, ...): incoming packet: #{inspect packet}"
:gen_tcp.send(socket, "****#{packet}*****")
{:noreply,state}
end
def handle_info({:tcp_closed, _socket}, state) do
Logger.info("handle_info({:tcp_closed, ...): Client closed socket.")
{:noreply, state}
end
def handle_info({:tcp_error, socket, reason}, state) do
Logger.info("Connection closed due to #{reason}: #{socket}")
{:noreply,state}
end
end
To start the server:
~/elixir_programs/tcp_server$ iex -S mix
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Interactive Elixir (1.8.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> TcpServer.start_link()
gen_tcp is listening on port: 6666
In another terminal window:
~/python_programs$ cat 5.py
import socket
port = 6666
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("127.0.0.1", port))
s.send("\x02Test\x03".encode())
print(s.recv(1024))
~/python_programs$ p36 5.py
b'****\x02Test\x03*****'
~/python_programs$
As you can see, there was no duplication of the \x02 and \x03 characters.
Back in the server window:
{:ok, #PID<0.123.0>}
iex(2)>
21:54:18.363 [info] handle_info(:tcp, ...): incoming packet: <<2, 84, 101, 115, 116, 3>>
21:54:18.369 [info] handle_info({:tcp_closed, ...): Client closed socket.
Do you have another process that is the controlling process and is calling :gen_server.call({:tcp, socket, packet})?
By the way, in this code:
def handle_info({:tcp,socket,packet},state) do
packet may be the whole packet, 1/2 the packet or 1/10 of the packet. That's the way sockets work. The config option {packet, 0} tells gen_tcp that there is no length header (0 bytes) on the front of the packet, while {packet, 1|2|4} tells gen_tcp that the length of the packet is contained in the first byte, the first 2 bytes, or the first 4 bytes respectively. That way gen_tcp can read the first 1|2|4 bytes to get the packet length, say L, then keep reading from the socket until it has received L bytes. Then gen_tcp packages up the pieces into one message, and sends the whole message to the gen_server. On the other hand, when you specify {packet, 0}, you are telling gen_tcp that the packet has no length header; and because a socket splits up a single packet into an indeterminate number of chunks, gen_tcp has no idea where the end of the packet is, so gen_tcp's only option is to read a chunk from the socket and send the chunk to the gen_server; then read another chunk and send the chunk to the gen_server, etc., etc. That means the gen_server has to figure out where the end of the packet is.
Therefore, your server and your client have to agree on a protocol to signal the end of a packet; and handle_info(:tcp, ...) will have to store the pieces of the packet in the state (or in a db) until it has read all the chunks that make up a packet.
One protocol that you can use to signal the end of the packet is: the client closes the socket. In that case,
def handle_info({:tcp_closed, _socket}, state)
will be called, and inside that function clause you can assemble the chunks stored in state (or in a db) into a complete message, then do whatever is necessary, e.g. send the message back to the client.
If you use STX and ETX as your begin message, end message protocol, then handle_info(:tcp, ...) will still have to look for the STX character to signal that it should start storing chunks in state, and when handle_info(:tcp, ...) finds a chunk with an ETX character in it, then you have to assemble the entire message.

Python simple UDP server does not receive message with IP options

I have a simple udp server/client setup where I send a message from the client and print it on the server. This works well for a regular IP packet but the message is not received when I add an IP options header to the packet, even though I can sniff the packet using scapy.
Here's the packet without IP options
###[ Ethernet ]###
dst = 00:04:00:00:04:01
src = 00:aa:00:02:00:04
type = 0x800
###[ IP ]###
version = 4L
ihl = 5L
tos = 0x0
len = 47
id = 1
flags =
frag = 0L
ttl = 61
proto = udp
chksum = 0x62f4
src = 10.0.2.101
dst = 10.0.4.101
\options \
###[ UDP ]###
sport = 10001
dport = 3478
len = 27
chksum = 0x2bd1
###[ Raw ]###
load = 'message from a game'
And here's the packet with IP options header:
###[ Ethernet ]###
dst = 00:04:00:00:04:01
src = 00:aa:00:02:00:04
type = 0x800
###[ IP ]###
version = 4L
ihl = 8L
tos = 0x0
len = 59
id = 1
flags =
frag = 0L
ttl = 61
proto = udp
chksum = 0x5fe8
src = 10.0.2.101
dst = 10.0.4.101
\options \
|###[ IPOption ]###
| copy_flag = 1L
| optclass = control
| option = 31L
| length = 12
| value = '\x00\x01\x00\x00RTGAME'
###[ UDP ]###
sport = 10001
dport = 3478
len = 27
chksum = 0x2bd1
###[ Raw ]###
load = 'message from a game'
And here's the UDP server:
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(('', args.port))
while True:
try:
data, addr = sock.recvfrom(1024)
print("received: %s" % data)
except KeyboardInterrupt:
sock.close()
break
I've been stuck on this for a few days and would love if someone could figure it out.
Thanks
have just been playing and the following works as a self-contained/minimal working example for me with Python 3.7.1 under both OSX and Linux
generating a valid set of IP Options:
from scapy.all import IPOption, raw
ipopts = raw(IPOption(
copy_flag=1, optclass='control', option=31,
value='\x00\x01\x00\x00RTGAME'))
(if you don't have Scapy, the above should generate: b'\x9f\x0c\x00\x01\x00\x00RTGAME')
client code:
import socket
from time import sleep
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
s.connect(('127.0.0.1', 3478))
s.setsockopt(socket.IPPROTO_IP, socket.IP_OPTIONS, ipopts)
while True:
s.send(b'message from a game')
sleep(1)
server code:
import socket
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
s.bind(('', 3478))
s.setsockopt(socket.IPPROTO_IP, socket.IP_RECVOPTS, 1)
while True:
print(*s.recvmsg(4096, 1024))
this should result in the "server" displaying lines like:
b'message from a game\n' [(0, 6, b'\x9f\x0c\x00\x01\x00\x00RTGAME')] 0 ('127.0.0.1', 46047)
furthermore, I can watch packets move over the network by running:
sudo tcpdump -i lo0 -vvv -n 'udp and port 3478'
at the command line, or this in Scapy:
sniff(iface='lo0', filter='udp and port 3478', prn=lambda x: x.show())
for some reason I don't actually receive the ancillary data containing the IP Options under OSX, but the data shows up in the packet sniffers.
The problem was due to an incorrect IPv4 checksum. I failed to mention in the question that I'm running this in a mininet environment with custom switches. The IP options get added in transit by a switch, but the checksum wasn't updated. Once I fixed that, the packet made it to the server.
Thanks for the help and pointers everyone!

Reading MLDv2 queries using an IPv6 socket

I have mrd6 installed on my raspberry pi. It registers with a local interface (tun0) and periodically transmits MLDv2 queries over it.
According to [RFC3810], MLDv2 message types are a subset of ICMPv6 messages, and are identified in IPv6 packets by a preceding Next Header value of 58 (0x3a). They are sent with a link-local IPv6 Source Address, an IPv6 Hop Limit of 1, and an IPv6 Router Alert option [RFC2711] in a Hop-by-Hop Options header.
I can confirm that I'm seeing these packets periodically over tun0:
pi#machine:~ $ sudo tcpdump -i tun0 ip6 -vv -XX
01:22:52.125915 IP6 (flowlabel 0x71df6, hlim 1, next-header Options (0)
payload length: 36)
fe80::69bf:be2d:e087:9921 > ip6-allnodes: HBH (rtalert: 0x0000) (padn)
[icmp6 sum ok] ICMP6, multicast listener query v2 [max resp delay=10000]
[gaddr :: robustness=2 qqi=125]
0x0000: 6007 1df6 0024 0001 fe80 0000 0000 0000 `....$..........
0x0010: 69bf be2d e087 9921 ff02 0000 0000 0000 i..-...!........
0x0020: 0000 0000 0000 0001 3a00 0502 0000 0100 ........:.......
0x0030: 8200 b500 2710 0000 0000 0000 0000 0000 ....'...........
0x0040: 0000 0000 0000 0000 027d 0000 .........}..
I have a socket set up in my application on tun0 as follows, since I expect these to be ICMP packets:
int fd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); // ICMP
// ... bind this socket to tun0
int interfaceIndex = // tun0 interface Index
int mcastTTL = 10;
int loopBack = 1;
if (setsockopt(listener->socket,
IPPROTO_IPV6,
IPV6_MULTICAST_IF,
&interfaceIndex,
sizeof(interfaceIndex))
< 0) {
perror("setsockopt:: IPV6_MULTICAST_IF:: ");
}
if (setsockopt(listener->socket,
IPPROTO_IPV6,
IPV6_MULTICAST_LOOP,
&loopBack,
sizeof(loopBack))
< 0) {
perror("setsockopt:: IPV6_MULTICAST_LOOP:: ");
}
if (setsockopt(listener->socket,
IPPROTO_IPV6,
IPV6_MULTICAST_HOPS,
&mcastTTL,
sizeof(mcastTTL))
< 0) {
perror("setsockopt:: IPV6_MULTICAST_HOPS:: ");
}
struct ipv6_mreq mreq6 = {{{{0}}}};
MEMCOPY(&mreq6.ipv6mr_multiaddr.s6_addr, sourceAddress, 16);
mreq6.ipv6mr_interface = interfaceIndex;
if (setsockopt(listener->socket,
IPPROTO_IPV6,
IPV6_JOIN_GROUP,
&mreq6,
sizeof(mreq6))
< 0) {
perror("setsockopt:: IPV6_JOIN_GROUP:: ");
}
Setting up the socket this way, I can receive ICMP echo requests, replies to my own address, and multicasts sent using the link-local multicast address. However, I don't see any MLDv2 queries.
Here's my receive loop:
uint8_t received[1000] = { 0 };
struct sockaddr_storage peerAddress = { 0 };
socklen_t addressLength = sizeof(peerAddress);
socklen_t addressLength = sizeof(peerAddress);
int receivedLength = recvfrom(sockfd,
received,
sizeof(received),
0,
(struct sockaddr *)&peerAddress,
&addressLength);
if (receivedLength > 0) {
// Never get here for MLDv2 queries.
}
Researching this a bit further, I discovered the IPV6_ROUTER_ALERT socket option, which the man page describes as follows:
IPV6_ROUTER_ALERT
Pass forwarded packets containing a router alert hop-by-hop option to this socket.
Only allowed for SOCK_RAW sockets. The tapped packets are not forwarded by the
kernel, it is the user's responsibility to send them out again. Argument is a
pointer to an integer. A positive integer indicates a router alert option value
to intercept. Packets carrying a router alert option with a value field
containing this integer will be delivered to the socket. A negative integer
disables delivery of packets with router alert options to this socket.
So I figured I was missing this option, and tried setting it as follows. [RFC2710] 0 means Multicast Listener Discovery message.
int routerAlertOption = 0;
if (setsockopt(listener->socket,
IPPROTO_IPV6,
IPV6_ROUTER_ALERT,
&routerAlertOption,
sizeof(routerAlertOption))
< 0) {
perror("setsockopt:: IPV6_ROUTER_ALERT:: ");
}
However, this gives me the ENOPROTOOPT error (errno 92). Some more Googling (http://www.atm.tut.fi/list-archive/usagi-users-2005/msg00317.html) led me to the fact that you can't set the IPV6_ROUTER_ALERT option with the IPPROTO_ICMPV6 protocol. It needs a socket defined using the IPPROTO_RAW protocol.
However, defining my socket as:
int fd = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW);
means I'm not able to receive any ICMP packets in my recvfrom anymore.
TL;DR: How do I read MLDv2 queries using an IPv6 socket?
edit (answer):
It appears conventional implementations of Linux will drop MLDv2 packets when passing them to an ICMPV6 socket. Why this is, I'm not sure. (Could be because of the next-header option.)
I followed the accepted answer below and went with an approach of reading raw packets on the tun0 interface. I followed the ping6_ll.c example here: http://www.pdbuchan.com/rawsock/rawsock.html.
It uses a socket with (SOCK_RAW, ETH_P_ALL). You can also set some SOL_PACKET options to filter on specific multicast rules on your interface.
From a quick look at RFCs things aren't looking good. Per RFC4443 (ICMPv6) 2.4:
2.4. Message Processing Rules
Implementations MUST observe the following rules when processing
ICMPv6 messages (from [RFC-1122]):
(b) If an ICMPv6 informational message of unknown type is received,
it MUST be silently discarded.
According to MLDv2 spec it makes use of types 130, 143, perhaps something else (not seeing more diagrams in the RFC), while valid ICMPv6 types are 1, 2, 3, 4, 101, 107, 127, 128, 129, 200, 201, 255.
It looks like the implementation (kernel) must drop MLDv2 packets if they are to be passed to an ICMPv6 socket. Personally I don't see much sense in making MLDv2 look like ICMPv6 if conventional implementations will drop the packet anyways, but I didn't see anything that contradicts this claim.
You can surely go deeper and use a raw socket, especially given that your stack doesn't recognize MLDv2 (perhaps there's a kernel patch to fix that?). But you'll have to parse IP and ICMP headers on your own then.

packetsocket opened on loopback device receives all the packets twice. How to filter these duplicate entries?

when i open a packetsocket on a loopback interface (lo) and listen all the packets are seen twice. why is it so?
But a capture on the interface using tcpdump correctly ignores the duplicate entries. see the 'packets received by filter' (which contains the duplicate packets) and 'packets captured'. How is this filtering done
tcpdump -i lo -s 0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on lo, link-type EN10MB (Ethernet), capture size 65535 bytes
11:00:08.439542 IP 12.0.0.3 > localhost.localdomain: icmp 64: echo request seq 1
11:00:08.439559 IP localhost.localdomain > 12.0.0.3: icmp 64: echo reply seq 1
11:00:09.439866 IP 12.0.0.3 > localhost.localdomain: icmp 64: echo request seq 2
11:00:09.439884 IP localhost.localdomain > 12.0.0.3: icmp 64: echo reply seq 2
11:00:10.439389 IP 12.0.0.3 > localhost.localdomain: icmp 64: echo request seq 3
11:00:10.439410 IP localhost.localdomain > 12.0.0.3: icmp 64: echo reply seq 3
6 packets captured
12 packets received by filter
0 packets dropped by kernel
My code:
int main()
{
int sockFd;
if ( (sockFd=socket(PF_PACKET, SOCK_DGRAM, 0))<0 ) {
perror("socket()");
return -1;
}
/* bind the packet socket */
struct sockaddr_ll addr;
struct ifreq ifr;
strncpy (ifr.ifr_name, "lo", sizeof(ifr.ifr_name));
if(ioctl(sockFd, SIOCGIFINDEX, &ifr) == -1)
{
perror("iotcl");
return -1;
}
memset(&addr, 0, sizeof(addr));
addr.sll_family=AF_PACKET;
addr.sll_protocol=htons(ETH_P_ALL);
addr.sll_ifindex=ifr.ifr_ifindex;
if ( bind(sockFd, (struct sockaddr *)&addr, sizeof(addr)) ) {
perror("bind()");
return -1;
}
char buffer[MAX_BUFFER+1];
int tmpVal = 1;
while(tmpVal > 0)
{
tmpVal = recv (sockFd, buffer, MAX_BUFFER, 0);
cout<<"Received Pkt with Bytes "<<tmpVal <<endl;
}
}
Figured out the problem.
from libcaps code:
* - The loopback device gives every packet twice; on 2.2[.x] kernels,
* if we use PF_PACKET, we can filter out the transmitted version
* of the packet by using data in the "sockaddr_ll" returned by
* "recvfrom()", but, on 2.0[.x] kernels, we have to use
* PF_INET/SOCK_PACKET, which means "recvfrom()" supplies a
* "sockaddr_pkt" which doesn't give us enough information to let
* us do that.
the listening entity needs to filter the duplicate packet using the if_index got from recvfrom api.