Welcome to my blog! Let me introduce a little bit about myself. It's been about 3 years since I started Javascript for building my first blog which was about freestyle football (my videos can be found here in case if interested haha). Eventually I ended up with Linux kernel and now I really enjoying working on it. I am starting to realize that things are getting more an more interesting and exciting as I learn more! Although all the stuff I write here is intended as my personal note, you can contact me below if you have any questions. https://x.com/RyoTaka58930896
Hi! I decided to make use of this website as my new blog about things I learn in everyday life. I used to write a blog on freestyle football and I really enjoyed keeping track of my progress and I decided to do the same this time as well. Below is the link to my previous blog. https://illinoisfslife.com/ Anyways, I'm soooo excited about getting into IT and learning about technologies essential in today's world!
This website was built for freestylers in the US to help connect regardless of our style, location, social media, etc. Anyone with a passion for freestyle soccer is welcome and hope this website helps find whatever you are looking for in freestyle!
Hi! I wanted to share something that I have been looking into related to Realtime kernel. RT kernel is a kernel which aims to achieve low latency for process scheduling. One of its application is audio and I'm trying to see how it benefits from RT kernel. Below is the setup that I'm currently working on. Machine: Mac M2 (which I bought it recently, it runs on Linux😆) Audio Editor: Ardour Although I'm still trying to understand how Ardour and audio input devices work, today I went to my friend's place who plays guitar to see how it works. It was quite intersting to see his setup, especially the audio interface which converts sound signals from guitar into digital bits so that PCs can do interesting suff (please correct me if I were wrong...). I am not yet ready to do any kind of testing(benchmarking?), but my guess is that higher the frequency you try to capture, you will need a kernel with a lower latency! By the way, besides playing around with guitar, Today I made a strawberry pi for the first time. I tasted ok but yet there remained a room for improvement. Maybe I can give another try next week😋. See you on the next post!
Hi, I just wanted to make a brief post (its new year). At the end of the last year, I was on travel to India, the country I always wanted to visit since college(couldn't due to covid). During the trip, I had a serious food poisoning. But I still consider it was a great trip(even though I still haven't had curry ever since I got back) and I am already looking forward to the next trip to India(喉元過ぎれば熱さ忘れる). I had a good time giving time for myself to think about this and that. Throughout the last year, I was trying to figure out this and that, and I believe now I at least have a direction. And I just feel so greatful, given the chance to pursuit what I want.
Hi, recently I have been looking into how user processes are prepared. Today's post is about the how user process and thread are being prepared in terms of allocated memory.
When a new process or thread being created, it calls clone system call. If you want to create a thread, you can pass CLONE_VM flag to the system call. One of the difference between a process and thread is that wheather it shares its memory space or not. The latter does but the former does not, and today's post is about looking into this difference.
In order to see the difference, I prepared a program which ① the parents process assignes a variable with 5, and ② the cloned child process rewrites the variable with 10. The difference mentioned earlier can be seen at the ② point.
Let's first see how the process looks like. Below shows that the PID 1628 is the parent and the 1629 is the child.
crash> ps | grep clone
1628 1601 0 ffff94fd4a271c80 IN 0.1 2628 1336 clone
1629 1628 0 ffff94fd4123b900 IN 0.1 2628 884 clone
crash>
Now let's take a look at the physical address correspoding to the variable's virtual address which is 0x404068. You can see the corresponding physical address is 17bfe068. As expected, the value at the address is still 5 as the child process does not share the address space.
crash> vtop -c ffff94fd4a271c80 404068
VIRTUAL PHYSICAL
404068 17bfe068
...
crash> rd -p 17bfe068 10
17bfe068: 0000000000000005 00000000014b72b0 .........rK.....
...
crash>
However, if you take a look at the threading, you will see the varible is now changed as the child shares the same address space and rewrites the variable.
crash> ps | grep clone
1714 1601 0 ffff94fd44e2d580 IN 0.1 2628 1396 clone
1715 1714 0 ffff94fd41291c80 IN 0.1 2628 964 clone
crash> vtop -c ffff94fd44e2d580 404068
VIRTUAL PHYSICAL
404068 1161e068
...
crash> rd -p 1161e068 10
1161e068: 000000000000000a 00000000004c32b0 .........2L.....
...
crash>
That's it for the today's post. Note that I used x86 for this experiment as vtop on arm64(below) did not work. I wonder why🤔. Might be fun looking into it.
crash> vtop -c 11884 aaaae706c84c
VIRTUAL PHYSICAL
vtop: invalid structure member offset: mm_struct_mmap
FILE: memory.c LINE: 4007 FUNCTION: vm_area_dump()
[/usr/bin/crash] error trace: aaaae706c84c => aaaae706456c => aaaae7101180 => aaaae71010ec
vtop: invalid structure member offset: mm_struct_mmap
FILE: memory.c LINE: 4007 FUNCTION: vm_area_dump()
crash>
Hi, I wanted make a post on how the first process gets initiated as I have been playing around with qemu recently. Specifically, I will describe how the parameters below lead to the /sbin/init, which has the pid of 1.
admin@ip-10-0-16-6:~/busybox/busybox$ qemu-system-aarch64 -M virt -cpu cortex-a72 -m 1024 -kernel /boot/vmlinuz-6.1.0-13-cloud-arm64 -initrd /home/admin/busybox/busybox/rootfs -append "root=/dev/ram0 rdinit=/sbin/init nokaslr" -nographic
Let's start with the source code. After the kernel finishes the initilization of this and that, it gets ready for the first process and calls rest_init. The function inits the rest and calls kernel_init, and then run_init_process gets called.
Now back to the arguments and see what it's doing. If you take a look at the man page of initrd, it says that -initrd gets uncompressed onto the /dev/ram0, which then mounted as the inital root file system. Here I found little confusing is that if you specify root as /dev/ram0, the initial root file system, which is /dev/ram0, will remain as the normal root file system.
Let's make sure that the first run_init_process is the one being called for initiating the first process in the case of using /dev/ram0 as the normal root file system. Here you can see that the execute_command has /sbin/init, the program specifed by rdinit from the normal root file system which now is the /dev/ram0.
(gdb) print ramdisk_execute_command
$1 = 0xffff00003fdf0196 "/sbin/init"
(gdb) print execute_command
$2 = 0x0
(gdb)
By the way, don't forget to specify "nokaslr" for the boot parameter. Otherwise, break points won't work😑. That is it for this post!
I have been trying to understand how systemtap works. And I want to share some of the things I studied about systemtap, or kprobe which systemtap uses internally.
When you register a handler through kprobe, it inserts brk instruction at the specified offset from the chosen symbol as shown below. You can find the sample program I used for testing kprobe here.
┌──(kali㉿kali-raspberry-pi)-[~/proper-version-name/dumpable-kernel]
└─$ sudo crash ./vmlinux
crash> dis cpus_are_stuck_in_kernel
0xffffffe9258290a4
Once it reaches the brk instruction, it triggers an el1 synchronous exception. In the case of brk instruction, EC will set to 0x3C and will call el1_dbg.
Where I found interesting is how brk_handler gets called. When I was first reading the source code, I thought brk_early64 is the one which gets called. However, tracing the functions showed that brk_handler is the one which was actually being called. Note that some of the functions doesn't appear because they are inlined...
┌──(kali㉿kali-raspberry-pi)-[~/systemtap/kprobe_observation]
└─$ sudo trace-cmd report -i trace.dat | grep "kexec_\|kimage_\|cpus_are_stuck_in_kernel\|do_debug_exception\|early_brk64\|do_bad\|brk_handler\|debug_exception_enter\|debug_exception_exit"
...
kexec-1060 [000] 246.092881: function: do_debug_exception
kexec-1060 [000] 246.092884: function: brk_handler
In addition, I found that the entry for the early_brk64 is replaced with brk_handler🤨.
crash> rd debug_fault_info
ffffffe926d6a168: ffffffe925831fe4 ...%....
crash> struct fault_info ffffffe926d6a168 10
fn = 0xffffffe925831fe4
If you trace back the brk_handler, you will find how this replacement happens. During the initialization, trap_init replaces the early_brk64 with brk_handler for the reason which I haven't found yet... That is it for today's post!
When I was trying to kexec on my RPI4, I had trouble loading the image. I remember that in order to diagnose the problem, I inserted bunch of printks to understand what was going on. Today I am writing about these 2 tools I recently started looking into which are used for debugging, maybe not just for debugging, called trace-cmd and systemtap.
Recalling back when I was tackling the problem of kexec not loading the image, my first approach was to strace kexec and trace the associated system calls. It looked something like below.
┌──(kali㉿kali-raspberry-pi)-[~]
└─$ sudo strace kexec -l /boot/kdumpv07222023.img --reuse-cmdline
...
kexec_load(0x2c9e6a0, 3, [{buf=0x7ff3c37010, bufsz=22475264, mem=0x1600000, memsz=23658496}, {buf=0x55555b7850, bufsz=55722, mem=0x2c90000, memsz=57344}, {buf=0x55555c57b0, bufsz=13656, mem=0x2c9e000, memsz=16384}], KEXEC_ARCH_AARCH64) = -1 EBUSY (Device or resource busy)
From what the strace above gave me, I started looking into the source code and inserted pritnks to see which functions are being called and what they are returning. Very clever haha🙃.
But now equipped with trace-cmd and systemtap, I think I'm released from this flooding of prinks, Phew... Let's say that I want to know which functions are being called. Considering the above case, I can use trace-cmd to trace all the functions being called and decide which of them might be the one that I am interested in.
┌──(kali㉿kali-raspberry-pi)-[~/systemtap]
└─$ trace-cmd report -i ebusy.dat | grep "kexec_\|kimage_\|cpus_are_stuck_in_kernel"
kexec-945 [000] 100.885692: function: kexec_image_load_default
...
kexec-945 [000] 100.886629: function: machine_kexec_prepare
kexec-945 [000] 100.886631: function: cpus_are_stuck_in_kernel
kexec-945 [000] 100.887281: function: kimage_free
...
When the system call for loading the kexec image is called, it calles either of kexec_load or kexec_file_load. The EBUSY error is returned by its sanity check which is handle by the cpus_are_stuck_in_kernel show above. You can guess that the cpus_are_stuck_in_kernel is the one that we are interested by reading the source code as the subsequent function are not being called.
https://github.com/raspberrypi/linux/blob/rpi-6.1.y/kernel/kexec_file.c#L368
https://github.com/raspberrypi/linux/blob/rpi-6.1.y/arch/arm64/kernel/machine_kexec.c#L70
https://github.com/raspberrypi/linux/blob/rpi-6.1.y/arch/arm64/kernel/smp.c#L1093
In order to make sure that cpus_are_stuck_in_kernel is the one preventing from loading the image, let's take a look at what its return value is. Here we can use systemtap, and below indeed shows the return value is EBUSY(-16).
┌──(kali㉿kali-raspberry-pi)-[~/systemtap]
└─$ sudo stap -v kexec.stp
Pass 1: parsed user script and 471 library scripts using 115208virt/100712res/6596shr/94160data kb, in 1860usr/360sys/2232real ms.
Pass 2: analyzed script: 1 probe, 2 functions, 0 embeds, 0 globals using 174920virt/161736res/7860shr/153872data kb, in 6930usr/700sys/7637real ms.
Pass 3: using cached /root/.systemtap/cache/07/stap_0744f24e121f251cab8e1f8c2089a20b_1355.c
Pass 4: using cached /root/.systemtap/cache/07/stap_0744f24e121f251cab8e1f8c2089a20b_1355.ko
Pass 5: starting run.
machine_kexec_prepare returned -16
Pass 5: run completed in 70usr/670sys/49763real ms.
I feel that what systemtap does is soo magical. And I'm not really comfortable using magic... Next I might look into how systemtap does its magic💡.
Hi! Today's post is about perf which is a tool I recently started looking into. perf monitors hardware/software events and is used for performace analysis. I have only started looking into the tool and haven't yet explored what its capable of but I will share something I found interesting about the tool for this post.
If you take a look at how perf collects hardware/software events, you will find that there is a hardware(Performance Monitoring Unit) dedicated for monitoring the system. PMU sends interrupts to check in the time(?) spent on CPU at function level. Here I will share the result of "perf top" on RPI4(arm64).
Samples: 10K of event 'cycles', 4000 Hz, Event count (approx.): 291421676 lost: 0/0 drop: 0/0
Overhead Shared Object Symbol
16.40% [kernel] [k] _raw_spin_unlock_irqrestore
7.16% [kernel] [k] finish_task_switch.isra.0
7.14% [kernel] [k] arch_counter_get_cntpct
4.03% [kernel] [k] _raw_spin_unlock_irq
3.60% [kernel] [k] _raw_read_unlock_irqr
And here is the result of "perf top" on Optiplex(x86).
Samples: 10K of event 'cycles', 4000 Hz, Event count (approx.): 297333264 lost: 0/0 drop: 0/0
Overhead Shared Object Symbol
4.45% [kernel] [k] native_sched_clock
1.88% perf [.] dso__find_symbol
1.64% perf [.] io__get_char
1.61% perf [.] rb_next
1.59% [kernel] [k] menu_select
...
One thing to note here is that you can see _raw_spin_unlock_irqrestore is spending a lot time on RPI4(arm64) unlike Optiplex(x86). If you recall that currently arm64 does not support NMI-like inturrupt, it's clear that why perf is showing such high overhead for _raw_spin_unlock_irqrestore.
Below shows the RPI4(arm64) and Optiplex(x86)'s interrupts triggered by PMU which is used for collecting data shown on "perf top". As Optiplex(x86)'s PMU uses NMI unlike RPI4(arm64), whenever it interrupts, its the exact function that is currently running at the moment. However, in the case of RPI4(arm64), the interrupts generated by PMU has the same priority as the currently runnning interrupt and needs to wait until it releases by calling _raw_spin_unlock_irqrestore.
┌──(kali㉿kali-raspberry-pi)-[~]
└─$ cat /proc/interrupts
CPU0 CPU1 CPU2 CPU3
...
29: 4111 0 0 0 GICv2 48 Level arm-pmu
30: 0 6871 0 0 GICv2 49 Level arm-pmu
31: 0 0 3374 0 GICv2 50 Level arm-pmu
32: 0 0 0 533 GICv2 51 Level arm-pmu
ryo@ryo-OptiPlex-3020:~$ cat /proc/interrupts
CPU0 CPU1 CPU2 CPU3
...
NMI: 7723 9494 7256 6888 Non-maskable interrupts
...
PMI: 7723 9494 7256 6888 Performance monitoring interrupts
That's it for this post. It's fun to take a look at different architecture other than arm64. It helps to deepen your understanding of cantain features(like interrupts) by making a comparison🧐.
I also wanted to share that I decided to make the memos private. Instead, my plan is to make posts more often here on this blog, See you on the next post!
Hi, today I will be posting about exceptions which is the subject I found very fun and interesting as it requires a good understanding of hardware along with the linux source code. I've yet only started exploring the subject but I'll share some of the stuff I've learned so far.
There are many situations that can trigger an exception such as system call, packet reception, etc... But for this post, I will specifically focus in the case of null pointer dereference😏.
As the MMU translation fault is an synchronous exception, it referes to the table and calls el1h_64_sync.
Whenever an exception is triggered, it saves the state of registers by storing them on the stack and passes the sp to the subsequent functions. You can see how el1h_64_sync stores the registers on the stack here.
crash> disas el1h_64_sync
Dump of assembler code for function el1h_64_sync:
0xffffffe8e681121c <+0>: stp x0, x1, [sp]
0xffffffe8e6811220 <+4>: stp x2, x3, [sp, #16]
...
0xffffffe8e6811278 <+92>: mov x0, sp
0xffffffe8e681127c <+96>: bl 0xffffffe8e739f080
After el1h_64_sync has done its job, in the case of null pointer dereference, it calles el1_abort by refereing to the ESR reigster which is 0x25. Then el1_abort refers to this table defined as fault_info which is used to trigger the appropriate handlers based on ESR register. The handler will be called based on the offset from the fault_info. In the case of Null pointer dereference, do_translation_fault will be triggered.
crash> sym fault_info
ffffffe833bcb000 (d) fault_info
crash> sym do_translation_fault
ffffffe833bae0b4 (t) do_translation_fault
crash> rd ffffffe833bcb000 0x20
ffffffe833bcb000: ffffffe8330377a0 0000008000000009 .w.3............
ffffffe833bcb010: ffffffe833e18a80 ffffffe8330377a0 ...3.....w.3....
ffffffe833bcb020: 0000008000000009 ffffffe833e18a98 ...........3....
ffffffe833bcb030: ffffffe8330377a0 0000008000000009 .w.3............
ffffffe833bcb040: ffffffe833e18ab8 ffffffe8330377a0 ...3.....w.3....
ffffffe833bcb050: 0000008000000009 ffffffe833e18ad8 ...........3....
ffffffe833bcb060: ffffffe833bae0b4 000000010000000b ...3............
ffffffe833bcb070: ffffffe833e18af8 ...
After the call of do_translation_fault, it simply calls functions accordingly. One thing to note is that once it gets to die, you can see that it won't panic unless you enable CONFIG_PANIC_ON_OOPS. That's it for this post! I will be digging deeper into this subject and might be sharing them here as well✍️.
Hi, last post I made was about "task_struct" which is used to manage tasks and I wrote little bit about how stacks are associated. In this post, I will share how to read a stack to trace the functions being called from a register being dumped at the moment of panic.
Here is an example of a dumped register that you will see in the event of panic. The registers of interest are frame pointer(x29) and program counter(x30).
[ 340.163740] pc : machine_kexec+0x44/0x200
[ 340.163757] lr : machine_kexec+0x44/0x200
[ 340.163773] sp : ffffffc00a963b50
[ 340.163782] x29: ffffffc00a963b50 x28: ffffff80bc6f3e00 x27: 0000000000000000
...
[ 340.163982] x2 : 0000000000000000 x1 : 0000000000000000 x0 : ffffff80bc6f3e00
In ARM64, whenever you branch(bl) to a new function, the instruction below gets called. It stores the fp and pc at the address where sp plus offset points to. As a result, fp is pointing at the address of the previous frame and it enables to track frames in a linked list fashion.
stp x29, x30, [sp, offset]!
Now let's take a look at the actual stack. I won't dump the entire stack👾, but take a look here if interested.
If you start from ffffffc00a963b50(X29), you will get a list of fps shown below.
ffffffc00a963b50
ffffffc00a963b80
ffffffc00a963d10
ffffffc00a963df0
ffffffc00a963e00
ffffffc00a963e30
ffffffc00a963e70
ffffffc00a963e80
ffffffc00a963ea0
ffffffc00a963fe0
0000000000000000
And as you can see from the stp instruction, the return addresses which points to the functions that we are intrested in, are on the stack right next to the fps. If we trace the addresses on the stack next to the fps and look up the associated functions, we get the backtrace.
crash> sym ffffffdefb15fa74
ffffffdefb15fa74 (T) __crash_kexec+164
ffffffdefbb8dd14 (T) panic+444
ffffffdefb0aaef0 (T) __arm64_sys_p4ni9+64
ffffffdefb029a60 (t) invoke_syscall+80
ffffffdefb029b9c (t) el0_svc_common+108
ffffffdefb029c98 (T) do_el0_svc+56
ffffffdefbb9b9d4 (t) el0_svc+48
ffffffdefbb9be28 (T) el0t_64_sync_handler+244
ffffffdefb011548 (t) el0t_64_sync+396
As you can see, this trace show that the panic was triggered by an user space exception. For the next post, I will write about how excpetions are triggered and how they work😉.
Hi, I have been working on kernel dump which works after the call of panic(). Along the way, I learned a lot about kernel basics and I decided to share some of them here as my memos are getting quite messy.
For this post, I will share how kernel manages tasks using the structure task_struct. You can find the source below.
https://elixir.bootlin.com/linux/latest/source/arch/arm64/include/asm/thread_info.h#L24
Here is an example of the task "Softwar~cThread" running at the moement of panic. You can see the struct task_struct has an address of ffffff805ea4be00 given at "TASK".
crash> bt -c 1
PID: 2132 TASK: ffffff805ea4be00 CPU: 1 COMMAND: "Softwar~cThread"
#0 [ffffffc00800bd60] crash_save_cpu at ffffffdefb15ff60
...
If you look at the definition of task_struct, there are some members that might be interesting to take a look at. The member that caught my attention is stack which points to the stack for the task.
crash> struct task_struct ffffff805ea4be00
struct task_struct {
thread_info = {
...
},
__state = 0,
stack = 0xffffffc00a110000,
If you take a look at the memory where the "stack" points at, you can see the exact stack at the moment of panic.
crash> rd ffffffc00a110000 0x1FFF
ffffffc00a110000: 0000000057ac6e9d 0000000000000000 .n.W............
ffffffc00a110010: 0000000000000000 0000000000000000 ................
ffffffc00a110020: 0000000000000000 0000000000000000 ................
...
ffffffc00a113f90: 0000007f5869bfa0 0000007f5eb5e100 ..iX.......^....
ffffffc00a113fa0: 0000007ff7c1d8a8 0000007f5eb5e100 ...........^....
ffffffc00a113fb0: 0000007ff7c1d8d4 0000000080000000 ................
ffffffc00a113fc0: 0000007feb8f2194 0000000000000062 .!......b.......
ffffffc00a113fd0: 0000000000000000 0000000000000000 ................
ffffffc00a113fe0: 0000000000000000 0000000000000000 ................
ffffffc00a113ff0: 0000000000000000 0000000000000000 rd: invalid kernel virtual address: ffffffc00a114000 type: "64-bit KVADDR"
crash>
One thing that I could't figure out is that I don't see thread_info at the bottom of the stack. I read a lot of articles explaining that each stack has a thread_info at its bottom... Maybe I should look deeper into how tasks are scheduled and stacks gets prepared😎.
Hi, I wanted to share something that I have been working on along with the networking stack, kernel crash dump. The reason why started working on the subject is because I quite often got stuck on debugging kernel panic and I need a tool besides inserting bunch of printks😅.
Along the way, I tried many different approches I found online and I finally made a kernel with functioning kexec in the event of kernel panic. Here is the source code that I prepared for ARM64(Raspberry Pi 4).
https://github.com/ryofslife/dumpable-kernel
You can find the details of implementation and all the researches that I have done here.
https://scrapbox.io/ryozioput/
I removed some parts of the code from the original source and added a system call which triggers a kernel panic for testing the kexec rebooting of the loaded kernel.
Besides the coding, I will share some of the tunings that you have to do if you want to enable kexec on Raspberry Pi(quad-core, 4GB RAM).
First, you have to make sure that you have reserved enough memory for the loaded kernel. You can check memory info on /proc/meminfo and estimate how much memory your system will need. If the memory you reserved was too small, it will hang and never boot up. For my system, 512MB was enough to get away with the boot up process. You might need more if you are running additional processes after the boot up.
┌──(kali㉿kali-raspberry-pi)-[~]
└─$ dmesg | grep crash
[ 0.000000] crashkernel reserved: 0x000000000ac00000 - 0x000000002ac00000 (512 MB)
[ 0.000000] Kernel command line: coherent_pool=1M 8250.nr_uarts=0 snd_bcm2835.enable_headphones=0 snd_bcm2835.enable_headphones=1 snd_bcm2835.enable_hdmi=1 bcm2708_fb.fbwidth=1824 bcm2708_fb.fbheight=984 bcm2708_fb.fbswap=1 smsc95xx.macaddr=E4:5F:01:D3:5B:D9 vc_mem.mem_base=0x3eb00000 vc_mem.mem_size=0x3ff00000 console=ttyS0,115200 console=tty1 root=PARTUUID=59536845-02 rootfstype=ext4 fsck.repair=yes rootwait net.ifnames=0 crashkernel=512M maxcpus=1
Another tip is that kexec did not work on multi-core mode at least on Raspberry Pi 4. Some of the changes that I made on the source code is that I disable the check against the number of running cores so that it can load the image regardless of the number of cores on the system. You can see that the maxcpu is set 1 on the above log from the dmesg.
I learned a lot from the process of preparing kexec and I enjoyed it. Kernel crash dump is such an insteresting subject, and I might be digging deeper into it👾.
Hi, today I wanted to make a short post that I now have a working kernel built-in ARP(reply) stack😎. Although the stack can't yet handle ARP request, my stack is now able to receive packets from local peers! Here are the output showing the windows sending arp request and raspberry pi (my stack!) replying to the request.
C:\WINDOWS\system32>arp -a
Interface: 192.168.10.2 --- 0x8
Internet Address Physical Address Type
192.168.10.1 fc-99-47-12-26-7a dynamic
192.168.10.255 ff-ff-ff-ff-ff-ff static
224.0.0.22 01-00-5e-00-00-16 static
224.0.0.251 01-00-5e-00-00-fb static
224.0.0.252 01-00-5e-00-00-fc static
239.255.255.250 01-00-5e-7f-ff-fa static
Interface: 172.21.176.1 --- 0x2b
Internet Address Physical Address Type
172.21.179.85 00-15-5d-4a-0e-39 dynamic
172.21.191.255 ff-ff-ff-ff-ff-ff static
224.0.0.22 01-00-5e-00-00-16 static
224.0.0.251 01-00-5e-00-00-fb static
224.0.0.252 01-00-5e-00-00-fc static
224.0.1.60 01-00-5e-00-01-3c static
239.255.255.250 01-00-5e-7f-ff-fa static
C:\WINDOWS\system32>ping 192.168.10.3
Pinging 192.168.10.3 with 32 bytes of data:
Request timed out.
Ping statistics for 192.168.10.3:
Packets: Sent = 1, Received = 0, Lost = 1 (100% loss),
Control-C
^C
C:\WINDOWS\system32>
┌──(kali㉿kali-raspberry-pi)-[~]
└─$ dmesg | grep my_arp_rcv
[ 93.757253] my_arp_rcv(): address of skb 00000000f1642941
[ 93.757324] my_arp_rcv(): address of arp header0000000063ca06a6
[ 93.757349] my_arp_rcv(): sender IP address of 34252992
[ 93.757371] my_arp_rcv(): target IP address of 51030208
[ 93.757391] my_arp_rcv(): the arp requsest is for IP protocol
[ 93.757430] my_arp_rcv(): found matching ip interface
[ 93.757484] my_arp_rcv(): successfully sent an arp response
C:\WINDOWS\system32>arp -a
Interface: 192.168.10.2 --- 0x8
Internet Address Physical Address Type
192.168.10.1 fc-99-47-12-26-7a dynamic
192.168.10.3 e4-5f-01-d3-5b-d9 dynamic --> here the entry is added, fc-99-47-12-26-7a is the MAC of pi
192.168.10.255 ff-ff-ff-ff-ff-ff static
224.0.0.22 01-00-5e-00-00-16 static
224.0.0.251 01-00-5e-00-00-fb static
224.0.0.252 01-00-5e-00-00-fc static
239.255.255.250 01-00-5e-7f-ff-fa static
Interface: 172.21.176.1 --- 0x2b
Internet Address Physical Address Type
172.21.179.85 00-15-5d-4a-0e-39 dynamic
172.21.191.255 ff-ff-ff-ff-ff-ff static
224.0.0.22 01-00-5e-00-00-16 static
224.0.0.251 01-00-5e-00-00-fb static
224.0.0.252 01-00-5e-00-00-fc static
224.0.1.60 01-00-5e-00-01-3c static
239.255.255.250 01-00-5e-7f-ff-fa static
C:\WINDOWS\system32>
I recently started this service called scrapbox.io for keeping all the notes taken while I build my programs. Below is the link to the note while I was doing some debuggings for this arp reply stack.
https://scrapbox.io/everydaymemo/arpリプライのデバッグ
Hi! There is this holiday called Golden Week in Japan. It is a week long holiday and I wanted to share this project I was working on for almost 12/7😅. Its about building your own networking stack from device(using TAP) to socket. It is a great project for those who want to understand how OS networking stack works behind the Linux kernel. Below is the github link I was following along the project.
https://github.com/pandax381/microps
https://github.com/sititou70/klab-protocol-stack-tutorial
I've gone through each stack and it really deepened my understanding of how OS handles data arriving to its physical devices and sending all the way up to sockets assigned to each application. I am thinking of sharing some of the key concepts of each stack and how they passes data to each other in the future posts! For today's post, I just wanted to share a modification that I made from the original program regarding the TCP stack. I enabled the TCP stack to be able to establish a active connection to a server on the internet and fetch some random content. Of course, the established connection can't just abandon the connection😏. It has to participate in the process of terminating connection passively. I modified the code to complete the entire procedure of such TCP connection. Below is the log from the local program, the one uses the DIY network stack.
00:18:18.434 [D] tcp_open_rfc793: connection established: local=192.0.2.2:7, foreign=194.195.86.83:8080 (tcp.c:1172)
00:18:18.434 [D] tcp_output_segment: 192.0.2.2:7 => 194.195.86.83:8080, len=100 (payload=80) (tcp.c:417)
src: 7
dst: 8080
seq: 1804289384
ack: 3543150099
off: 0x50 (20)
flg: 0x18 (---AP---)
wnd: 65535
sum: 0x30fe
up: 0
00:18:18.434 [D] ip_output_core: dev=net1, iface=192.0.2.2, protocol=TCP(0x06), len=120 (ip.c:477)
vhl: 0x45 [v: 4, hl: 5 (20)]
tos: 0x00
total: 120 (payload: 100)
id: 131
offset: 0x0000 [flags=0, offset=0]
ttl: 255
protocol: 6 (TCP)
sum: 0xdfe3 (0xdfe3)
src: 192.0.2.2
dst: 194.195.86.83
00:18:18.435 [D] arp_resolve: resolved, pa=192.0.2.1, ha=8e:6e:37:e9:d1:92 (arp.c:357)
00:18:18.435 [D] net_device_output: dev=net1, type=IP(0x0800), len=120 (net.c:189)
00:18:18.435 [D] ether_transmit_helper: dev=net1, type=IP(0x0800), len=134 (ether.c:108)
src: 00:00:5e:00:53:01
dst: 8e:6e:37:e9:d1:92
type: 0x0800 (IP)
Data with the size of 80 was sent. Closing the connection.
...
00:18:18.562 [D] tcp_input: 194.195.86.83:8080 => 192.0.2.2:7, len=67 (payload=47) (tcp.c:996)
src: 8080
dst: 7
seq: 3543150099
ack: 1804289464
off: 0x50 (20)
flg: 0x18 (---AP---)
wnd: 29200
sum: 0xc768
up: 0
+------+-------------------------------------------------+------------------+
| 0000 | 48 54 54 50 2f 31 2e 31 20 34 30 30 20 42 61 64 | HTTP/1.1 400 Bad |
| 0010 | 20 52 65 71 75 65 73 74 0d 0a 43 6f 6e 6e 65 63 | Request..Connec |
| 0020 | 74 69 6f 6e 3a 20 63 6c 6f 73 65 0d 0a 0d 0a | tion: close.... |
+------+-------------------------------------------------+------------------+
00:18:18.562 [D] tcp_output_segment: 192.0.2.2:7 => 194.195.86.83:8080, len=20 (payload=0) (tcp.c:417) src: 7
dst: 8080
seq: 1804289464
ack: 3543150146
off: 0x50 (20)
flg: 0x10 (---A----)
wnd: 65488
sum: 0x029d
up: 0
...
00:18:18.565 [D] tcp_input: 194.195.86.83:8080 => 192.0.2.2:7, len=20 (payload=0) (tcp.c:996)
src: 8080
dst: 7
seq: 3543150146
ack: 1804289464
off: 0x50 (20)
flg: 0x11 (---A---F)
wnd: 29200
sum: 0x905c
up: 0
00:18:18.565 [D] tcp_output_segment: 192.0.2.2:7 => 194.195.86.83:8080, len=20 (payload=0) (tcp.c:417) src: 7
dst: 8080
seq: 1804289464
ack: 3543150147
off: 0x50 (20)
flg: 0x10 (---A----)
wnd: 65535
sum: 0x026d
up: 0
...
00:18:18.565 [D] tcp_output_segment: 192.0.2.2:7 => 194.195.86.83:8080, len=20 (payload=0) (tcp.c:417) src: 7
dst: 8080
seq: 1804289464
ack: 3543150147
off: 0x50 (20)
flg: 0x11 (---A---F)
wnd: 65535
sum: 0x026c
up: 0
...
00:18:18.689 [D] tcp_input: 194.195.86.83:8080 => 192.0.2.2:7, len=20 (payload=0) (tcp.c:996)
src: 8080
dst: 7
seq: 3543150147
ack: 1804289465
off: 0x50 (20)
flg: 0x10 (---A----)
wnd: 29200
sum: 0x905b
up: 0
Connection is closed passively.
And here is the log from my web server hosting this web site. By comparing the logs, you can see that the TCP 3 way handshake at the beginning actively initiated by the local program of mine. At the end, the server is the one that initiating the termination of the connection as it has finished sending all the data, which in this case is a "400 Bad Request".... I made few modification on the program so that the local program can handle the state transition associated with the active establishment and the passive termination of a connection.
root@ryofslife:~# tcpdump src host 180.56.119.152 -vvv
...
15:18:18.426463 IP (tos 0x48, ttl 233, id 129, offset 0, flags [none], proto TCP (6), length 40)
p9209152-ipngn9901marunouchi.tokyo.ocn.ne.jp.60320 > ryofslife.com.http-alt: Flags [S], cksum 0xaed7 (correct), seq 1804289383, win 65535, length 0
...
15:18:18.550721 IP (tos 0x48, ttl 233, id 130, offset 0, flags [none], proto TCP (6), length 40)
p9209152-ipngn9901marunouchi.tokyo.ocn.ne.jp.60320 > ryofslife.com.http-alt: Flags [.], cksum 0xad84 (correct), seq 1804289384, ack 3543150099, win 65535, length 0
...
15:18:18.554528 IP (tos 0x48, ttl 233, id 131, offset 0, flags [none], proto TCP (6), length 120)
p9209152-ipngn9901marunouchi.tokyo.ocn.ne.jp.60320 > ryofslife.com.http-alt: Flags [P.], cksum 0xdb95 (correct), seq 0:80, ack 1, win 65535, length 80: HTTP, length: 80
GET / HTTP/1.1
...
15:18:18.679744 IP (tos 0x48, ttl 233, id 132, offset 0, flags [none], proto TCP (6), length 40)
p9209152-ipngn9901marunouchi.tokyo.ocn.ne.jp.60320 > ryofslife.com.http-alt: Flags [.], cksum 0xad34 (correct), seq 80, ack 48, win 65488, length 0
...
15:18:18.682063 IP (tos 0x48, ttl 233, id 133, offset 0, flags [none], proto TCP (6), length 40)
p9209152-ipngn9901marunouchi.tokyo.ocn.ne.jp.60320 > ryofslife.com.http-alt: Flags [.], cksum 0xad04 (correct), seq 80, ack 49, win 65535, length 0
15:18:18.682646 IP (tos 0x48, ttl 233, id 134, offset 0, flags [none], proto TCP (6), length 40)
p9209152-ipngn9901marunouchi.tokyo.ocn.ne.jp.60320 > ryofslife.com.http-alt: Flags [F.], cksum 0xad03 (correct), seq 80, ack 49, win 65535, length 0
15:18:18.683615 IP (tos 0x48, ttl 43, id 54157, offset 0, flags [none], proto TCP (6), length 52)
I am thinking of sharing the code on my github once done fixing little bits and pieces😎. Anyways, wish you all have a great week! See in the next post!!
Hi! I have been working on my lab and wanted to share its physical and logical topology. I think I made a huge improvement on the diagram compared to the last one from an aesthetic point of view😎. Here is the logical topology. There are networks connected by BGP and I prepared a local network for each one of them. All the three local networks are reachable from each other as they advertise their local network to their peers. Among the three routers, I installed netem to the VyOS so that I can simulate some of the WAN characteristics like latency and packet loss. And here is the physical setup and topology of my lab. I used two L2 switches to connect three routers and to prepare a local network for each of the routers. I recently took a simple benchmark regarding bandwidth and latency using this lab setup. It's great have an environment where you can gets your hands dirty😏. See you in the next post!
Hi! I've been running out of cables for my lab and I decided to make one by myself from a 15m CAT 6 ethernet cable which I got from my local store for about $10. I had to make 4 attempts to finally get a working cable and here are the mistakes that I made. Hope it helps someone who is trying to do the same thing😅. First attempt, the pin for white/orange wire were not properly attached. It is also not looking good as the cable sheet is too short for the plug😂. Second attempt, I misaligned the blue and white/green wire. Third attempt, I realized that I was configuring the order of the wires upside-down respect to the plug🤣🤣🤣. And here is the final product! Mistakes are proof that you are learning😎. See you in the next post!
Hi! I wanted to share that my lab is finally connected to the internet! For the last 6 months ever since I got my first switch and router, my lab never had gotten out of its LAN😅. It's the first time getting out of my LAN and interacting with the internet and I'm just sooo excited about it. The reason I decided to extend my lab to the internet is because I have been looking into technologies used over the internet, TOR in particular. I'm learning how relays used in TOR allows clients to hide their identity over the internet and how it helps people in some countries with strict censorship. I found the technology as well as the role it plays in society very interesting. I believe such technology to ensure people have access to learn what is happening around the world is crucial. Having such tool gives us additional choice in what we choose from the pool of sources and I believe it could make a huge different sometimes. I am learning that there are many ways that I can take part in the community and I'm sure there is something that I can do to help people out there! https://www.torproject.org/ By the way, I noticed that my lab topology is getting pretty messy😂. I'm currently working on BGP and I'll share a nicer topology here once I'm done with all the BGP configurations that I have in my mind. See you in the next post!!!!!
Hi! I have been working on l2tp on my lab and I wanted to leave a tip here as it took me about a week to finally make it work. I'm not sure if the problem that I faced is specific to the device that I am using, namely cisco 892 router, but here is the tip for those working on l2tp. When you are setting the interface which is used for tunneling the LAN, the one you apply "xconnect", it has to be a WAN interface! I found this very weird and confusing. Why would you want to use WAN interface for tunneling LAN interface🤣. The below link really saved me as the given answer was exactly the one I needed. Without it, I'm probably still working wondering why ping is not working even though that the tunnel is established and everything seems configured correctly. Cisco892で, "WANポート"を使わなければL2TP接続した両端の機器間でPing疎通できない I also used a lot of Switched Port Analyzer(SPAN) for debugging. It's very useful and fun tool for labbing! Below is the topology of my lab after setting up l2tp. I have been interested in benchmarking my networking devices for a while and I am hoping to make a post about it within a next couple week. Anyways, see you in the next post!
Hi! I have been working on my home lab for about 2 weeks and I wanted to share how it is going so far! I am trying to add as many things as possible that I learned on networking over the past 3 or 4 months. The topology is getting little messy but I'm thinking of organizing them once I'm done trying out all the things that I have in my mind😅. Below is the topology at the time of this post. And here are some of the info on the hardwares that appear on the topology. R1&R2: Cisco 892 VyOS: DELL Optiplex 3020 L3-1&L3-2: Catalyst 3560 L2-1&L2-2: Catalyst 2960 I'll be posting updates on my home lab whenever I learned anything interesting while working on it🧐. See you in the next post!
Hi! Today, I wanted to share that I installed VyOS on my DELL desktop as I needed additional router for my home lab. I usually install this kind of softwares on Ras Pi but I learned that there is no ARM64 version of VyOS😂. The reason I turned my DELL into VyOS is only because I wanted to try out BGP with physical devices😏. See you in the next post!
Hi! Today, I wanted to share about the things that I have learned by tunneling between Ras Pi and a router through GRE. At first, I was trying GRE on packet tracer but it didn't work for some reason. So I decided to instead work on physical devices which were available at the moment. I will share some of key commands for setting up GRE on Ras Pi. For the router, you will find many resources online for setting up GRE on a cisco router😅.
┌──(kali㉿kali-raspberry-pi)-[~]
└─$ sudo ip tunnel add gre1 mode gre remote 172.168.10.25 local 192.168.233.204 ttl 255
┌──(kali㉿kali-raspberry-pi)-[~]
└─$ ip tunnel list
gre0: gre/ip remote any local any ttl inherit nopmtudisc
gre1: gre/ip remote 172.168.10.25 local 192.168.233.204 ttl 255
┌──(kali㉿kali-raspberry-pi)-[~]
└─$ sudo ip addr add 10.10.10.1/24 dev gre1
┌──(kali㉿kali-raspberry-pi)-[~]
└─$ sudo ip link set gre1 up
┌──(kali㉿kali-raspberry-pi)-[~]
└─$ sudo ip addr add 192.168.233.204/24 dev eth0
The process of setting up GRE is the same for both Ras Pi and a router, only difference is the commands😎. Once done with the above set up, you have to configure the route for both the physical interface connected to the router and the virtual tunnel interface. Below is the route table.
┌──(kali㉿kali-raspberry-pi)-[~]
└─$ sudo ip route
default via 192.168.85.77 dev wlan0 proto dhcp src 192.168.85.76 metric 600
10.10.10.0/24 dev gre1 proto kernel scope link src 10.10.10.1
172.168.10.25 dev eth0 scope link
192.168.85.0/24 dev wlan0 proto kernel scope link src 192.168.85.76 metric 600
192.168.233.0/24 dev eth0 proto kernel scope link src 192.168.233.204
Here 10.10.10.0/24 is the network for the tunnel and 172.168.10.25 is the address of the router's physical interface connected to Ras Pi. When I was working on tunneling yesterday, I couldn't figure out why ping was failing after setting up the tunnel. It was the route info that was missing🤣. See you in the next post!
Hi! Today, I wanted to share that I recently turned my raspberry pi into to a firewall to make it part of my home lab. Here are some notes for setting up IPFire on raspberry pi. When I was setting up IPFire, I got stuck for a little while with a rainbow screen when trying to boot it up.
1. Set "hdmi_safe" to 0 which you will find in config.txt.
hdmi_safe=0
2. Set "SERIAL-CONSOLE" to OFF which you will find in uENV.txt.
SERIAL-CONSOLE=OFF
Hope it helps if any one of you encounters the same issue. Firewall is something that I haven't really looked into and excited about adding new component into my home lab and learning what it is capable of!
Hi! Today, I configured an EtherChannel to see how it affects STP. For the setup, I prepared two switches (Switch1 & Swtich2) and connected them with 2 cables. Below shows how STP is configured on Switch1 before EtherChannel is ready on Switch2.
Switch1(config)#
Switch1(config)#interface range fastEthernet 0/1-2
Switch1(config-if-range)#channel-group 1 mode active //Swtich1はactiveに設定
Creating a port-channel interface Port-channel 1
Switch1(config-if-range)#
Switch1(config-if-range)#channel-protocol LACP
Switch1(config-if-range)#exit
Switch1(config)#exit
Switch1#show
00:11:48: %SYS-5-CONFIG_I: Configured from console by console
% Type "show ?" for a list of subcommands
Switch1#show etherchannel summary
Flags: D - down P - in port-channel
I - stand-alone s - suspended
H - Hot-standby (LACP only)
R - Layer3 S - Layer2
U - in use f - failed to allocate aggregator
u - unsuitable for bundling
w - waiting to be aggregated
d - default port
Number of channel-groups in use: 1
Number of aggregators: 1
Group Port-channel Protocol Ports
------+-------------+-----------+-----------------------------------------------
1 Po1(SD) LACP Fa0/1(I) Fa0/2(I)
Switch1#
Switch1#show spanning-tree
VLAN0001
Spanning tree enabled protocol ieee
Root ID Priority 32769
Address ec30.918e.c100
This bridge is the root
Hello Time 2 sec Max Age 20 sec Forward Delay 15 sec
Bridge ID Priority 32769 (priority 32768 sys-id-ext 1)
Address ec30.918e.c100
Hello Time 2 sec Max Age 20 sec Forward Delay 15 sec
Aging Time 300
Interface Role Sts Cost Prio.Nbr Type
---------------- ---- --- --------- -------- --------------------------------
Fa0/1 Desg FWD 19 128.1 P2p
Fa0/2 Desg FWD 19 128.2 P2p
As you can see from the above, since the EtherChannel is still not configured on Switch2, you can see that STP recognizes 2 ports as they are not yet bundled even though the 2 ports are registered on the channel-group 1. Below is how Switch2 is configured after setting up Switch.
Switch1#
Switch1#show etherchannel summary
Flags: D - down P - in port-channel
I - stand-alone s - suspended
H - Hot-standby (LACP only)
R - Layer3 S - Layer2
U - in use f - failed to allocate aggregator
u - unsuitable for bundling
w - waiting to be aggregated
d - default port
Number of channel-groups in use: 1
Number of aggregators: 1
Group Port-channel Protocol Ports
------+-------------+-----------+-----------------------------------------------
1 Po1(SU) LACP Fa0/1(P) Fa0/2(P)
Switch1#show spanning-tree
VLAN0001
Spanning tree enabled protocol ieee
Root ID Priority 32769
Address ec30.918e.c100
This bridge is the root
Hello Time 2 sec Max Age 20 sec Forward Delay 15 sec
Bridge ID Priority 32769 (priority 32768 sys-id-ext 1)
Address ec30.918e.c100
Hello Time 2 sec Max Age 20 sec Forward Delay 15 sec
Aging Time 300
Interface Role Sts Cost Prio.Nbr Type
---------------- ---- --- --------- -------- --------------------------------
Po1 Desg FWD 12 128.56 P2p
After the ports are bundled, you can see that there are only 1 port recognized by STP. Another observation you can make is that the cost of the bundle port has decreased from 19 to 12. I also tested how STP will behave if I plugged out one of the port used by the channel-group.
Switch1#show etherchannel summary
Flags: D - down P - in port-channel
I - stand-alone s - suspended
H - Hot-standby (LACP only)
R - Layer3 S - Layer2
U - in use f - failed to allocate aggregator
u - unsuitable for bundling
w - waiting to be aggregated
d - default port
Number of channel-groups in use: 1
Number of aggregators: 1
Group Port-channel Protocol Ports
------+-------------+-----------+-----------------------------------------------
1 Po1(SU) LACP Fa0/1(P) Fa0/2(D)
Switch1#show spanning-tree
VLAN0001
Spanning tree enabled protocol ieee
Root ID Priority 32769
Address ec30.918e.c100
This bridge is the root
Hello Time 2 sec Max Age 20 sec Forward Delay 15 sec
Bridge ID Priority 32769 (priority 32768 sys-id-ext 1)
Address ec30.918e.c100
Hello Time 2 sec Max Age 20 sec Forward Delay 15 sec
Aging Time 300
Interface Role Sts Cost Prio.Nbr Type
---------------- ---- --- --------- -------- --------------------------------
Po1 Desg FWD 19 128.56 P2p
Comparing before and after plugging one of the port on Switch1 shows that the cost has decreased but it is still recognized as a port-channel.
Hi! Today, I tested STP with my two switches by simply connecting them with two cables. As shown below, one of the links belonging to the non-root bridge got blocked based on the priority number.
pi#show spanning-tree vlan 1
VLAN0001
Spanning tree enabled protocol ieee
Root ID Priority 32769
Address ec30.918e.c100
Cost 19
Port 5 (FastEthernet0/3)
Hello Time 2 sec Max Age 20 sec Forward Delay 15 sec
Bridge ID Priority 32769 (priority 32768 sys-id-ext 1)
Address f029.2952.8d80
Hello Time 2 sec Max Age 20 sec Forward Delay 15 sec
Aging Time 300 sec
Interface Role Sts Cost Prio.Nbr Type
------------------- ---- --- --------- -------- --------------------------------
Fa0/1 Desg FWD 19 128.3 P2p
Fa0/3 Root FWD 19 128.5 P2p
Fa0/5 Altn BLK 19 128.7 P2p
As the above shows, Fa0/3 is the root port connected to the switch on the other end. So I plugged it out to see if the connection remains up by changing the root port to Fa0/5. Below is the result.
pi>show spanning-tree vlan 1
VLAN0001
Spanning tree enabled protocol ieee
Root ID Priority 32769
Address ec30.918e.c100
Cost 19
Port 7 (FastEthernet0/5)
Hello Time 2 sec Max Age 20 sec Forward Delay 15 sec
Bridge ID Priority 32769 (priority 32768 sys-id-ext 1)
Address f029.2952.8d80
Hello Time 2 sec Max Age 20 sec Forward Delay 15 sec
Aging Time 15 sec
Interface Role Sts Cost Prio.Nbr Type
------------------- ---- --- --------- -------- --------------------------------
Fa0/1 Desg FWD 19 128.3 P2p
Fa0/5 Root LIS 19 128.7 P2p
As expected, Fa0/5 is now the root port and the connection is still up! However, one drawback of this configuration is that one of the links is left unused. In order to utilize both of the links, I can set up an etherchannel and bundle the two links. I'm currently studying etherchannel to actually test it out and see how it differs from simply connected redundant links🧐.
Hi! Today, I was working on Inter VLAN Routing using a router(800) and L2 switch(Catalyst 2960). I connected a PC to each VLAN(VLAN10, VLAN20) and tested if I can ping to each other. Below is the set up for the PCs(Windows, Raspberry Pi) connected to the switch. The ip address for the Windows PC is 192.168.1.2/24 and the gateway address for 192.168.2.0/24 is 192.168.1.1/24. For Raspberry Pi, its ip address is 192.168.2.2/24 and the gateway address for 192.168.1.0/24 is 192.168.2.1/24.
C:\WINDOWS\system32>route add 192.168.2.0 mask 255.255.255.0 192.168.1.1
OK!
┌──(kali㉿kali-raspberry-pi)-[~]
└─$ sudo ifconfig eth0 192.168.2.2
┌──(kali㉿kali-raspberry-pi)-[~]
└─$ sudo ip route add 192.168.1.0/255.255.255.0 via 192.168.2.1 dev eth0 onlink
┌──(kali㉿kali-raspberry-pi)-[~]
└─$ route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.164.146 0.0.0.0 UG 600 0 0 wlan0
192.168.1.0 192.168.2.1 255.255.255.0 UG 0 0 0 eth0
192.168.2.0 192.168.2.1 255.255.255.0 UG 0 0 0 eth0
192.168.164.0 0.0.0.0 255.255.255.0 U 600 0 0 wlan0
Then I configured the switch. I prepared vlan10 & vlan20 for the Windows PC & Raspberry Pi, respectively. After finishing setting up vlans, you need to assign ports to the respective vlans. This time, I used 4 cables, two for connecting the PCs to the switch and the other two for connecting the router. Here are the commands I used for creating vlans.
Switch>enable
Switch#config terminal
Enter configuration commands, one per line. End with CNTL/Z.
Switch(config)#vlan 10
Switch(config-vlan)#vlan 20
Switch(config-vlan)#do show vlan
VLAN Name Status Ports
---- -------------------------------- --------- -------------------------------
1 default active Fa0/1, Fa0/2, Fa0/3, Fa0/4
Fa0/5, Fa0/6, Fa0/7, Fa0/8
Fa0/9, Fa0/10, Fa0/11, Fa0/12
Fa0/13, Fa0/14, Fa0/15, Fa0/16
Fa0/17, Fa0/18, Fa0/19, Fa0/20
Fa0/21, Fa0/22, Fa0/23, Fa0/24
Gi0/1, Gi0/2
10 VLAN0010 active
20 VLAN0020 active
1002 fddi-default act/unsup
1003 token-ring-default act/unsup
1004 fddinet-default act/unsup
1005 trnet-default act/unsup
Next I assigned the ports to the respective vlans.
Switch(config)#do show vlan
VLAN Name Status Ports
---- -------------------------------- --------- -------------------------------
1 default active Fa0/1, Fa0/2, Fa0/3, Fa0/4
Fa0/5, Fa0/6, Fa0/7, Fa0/8
Fa0/9, Fa0/10, Fa0/11, Fa0/12
Fa0/13, Fa0/14, Fa0/15, Fa0/16
Fa0/17, Fa0/18, Fa0/19, Fa0/20
Fa0/21, Fa0/22, Fa0/23, Fa0/24
Gi0/1, Gi0/2
10 VLAN0010 active
20 VLAN0020 active
1002 fddi-default act/unsup
1003 token-ring-default act/unsup
1004 fddinet-default act/unsup
1005 trnet-default act/unsup
VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2
---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------
1 enet 100001 1500 - - - - - 0 0
10 enet 100010 1500 - - - - - 0 0
20 enet 100020 1500 - - - - - 0 0
1002 fddi 101002 1500 - - - - - 0 0
Switch(config)#interface fa0/1
Switch(config-if)#
00:02:41: %CDP-4-NATIVE_VLAN_MISMATCH: Native VLAN mismatch discovered on FastEthernet0/3 (1), with Router FastEthernet0 (10).
Switch(config-if)#
00:02:47: %CDP-4-NATIVE_VLAN_MISMATCH: Native VLAN mismatch discovered on FastEthernet0/5 (1), with Router FastEthernet1 (20).
Switch(config-if)#switchport mode access
Switch(config-if)#switchport access vlan 10
Switch(config-if)#interface fa0/2
Switch(config-if)#switchport mode access
Switch(config-if)#switchport access vlan 20
Switch(config-if)#
00:03:36: %CDP-4-NATIVE_VLAN_MISMATCH: Native VLAN mismatch discovered on FastEthernet0/3 (1), with Router FastEthernet0 (10).
Switch(config-if)#interface fa0/3
Switch(config-if)#switchport access vlan 20
00:03:42: %CDP-4-NATIVE_VLAN_MISMATCH: Native VLAN mismatch discovered on FastEthernet0/5 (1), with Router FastEthernet1 (20).
Switch(config-if)#switchport mode access
Switch(config-if)#switchport access vlan 10
Switch(config-if)#interface fa0/5
Switch(config-if)#switchport mode access
Switch(config-if)#switchport access vlan 20
Switch(config-if)#switchport access vlan 20
00:04:10: %LINEPROTO-5-UPDOWN: Line protocol on Interface Vlan1, changed state tmode access
Switch(config-if)#do show vlan
VLAN Name Status Ports
---- -------------------------------- --------- -------------------------------
1 default active Fa0/4, Fa0/6, Fa0/7, Fa0/8
Fa0/9, Fa0/10, Fa0/11, Fa0/12
Fa0/13, Fa0/14, Fa0/15, Fa0/16
Fa0/17, Fa0/18, Fa0/19, Fa0/20
Fa0/21, Fa0/22, Fa0/23, Fa0/24
Gi0/1, Gi0/2
10 VLAN0010 active Fa0/1, Fa0/3
20 VLAN0020 active Fa0/2, Fa0/5
1002 fddi-default act/unsup
1003 token-ring-default act/unsup
1004 fddinet-default act/unsup
1005 trnet-default act/unsup
VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2
---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------
1 enet 100001 1500 - - - - - 0 0
10 enet 100010 1500 - - - - - 0 0
20 enet 100020 1500 - - - - - 0 0
1002 fddi 101002 1500 - - - - - 0 0
1003 tr 101003 1500 - - - - - 0 0
After working on the switch, I had to configure the ports on the router each connected to the switch's vlan port. At first, I tried to assign IP address to the ports directly but I got this error.
Router#config terminal
Enter configuration commands, one per line. End with CNTL/Z.
Router(config)#interface fastethernet0
Router(config-if)#ip address 192.168.1.1 255.255.255.0
% IP addresses may not be configured on L2 links.
The problem was that the port which I was configuring only works with L2 layer and an IP address can't be assigned. After doing some research, I figured out that I need to first create a vlan so that I can assign an IP address to it. The created vlan with an IP address can then be assigned to a port of your choice. Here is how I did it.
Router(config)#vlan 10
Router(config-vlan)#vlan 20
Router(config-vlan)#interface vlan 10
Router(config-if)#ip address 192.168.1.1 255.255.255.0
% 192.168.1.0 overlaps with Vlan1
Router(config-if)#interface vlan 1
Router(config-if)#ip address 192.168.3.1 255.255.255.0
Router(config-if)#interface vlan 10
Router(config-if)#ip address 192.168.1.1 255.255.255.0
Router(config-if)#no shut
Router(config-if)#interface vlan 20
Router(config-if)#
*Jan 3 09:40:49.434: %LINEPROTO-5-UPDOWN: Line protocol on Interface Vlan20, changed state to down
Router(config-if)#ip address 192.168.2.1 255.255.255.0
% 192.168.2.0 overlaps with Vlan2
Router(config-if)#interface vlan 2
Router(config-if)#ip address 192.168.4.1 255.255.255.0
Router(config-if)#interface vlan 20
Router(config-if)#ip address 192.168.2.1 255.255.255.0
Router(config-if)#no shut
Router(config-if)#interface fastethernet 1
Router(config-if)#switchport access vlan 10
Router(config-if)#interface fastethernet 2
Router(config-if)#switchport access vlan 20
Router(config-if)#exit
Router(config)#
Router(config)#do show interface (| include Vlan)
...
Vlan10 is up, line protocol is up
Hardware is EtherSVI, address is e4d3.f166.3fd2 (bia e4d3.f166.3fd2)
Internet address is 192.168.1.1/24
MTU 1500 bytes, BW 100000 Kbit/sec, DLY 100 usec,
reliability 255/255, txload 1/255, rxload 1/255
...
Vlan20 is up, line protocol is up
Hardware is EtherSVI, address is e4d3.f166.3fd2 (bia e4d3.f166.3fd2)
Internet address is 192.168.2.1/24
MTU 1500 bytes, BW 100000 Kbit/sec, DLY 100 usec,
reliability 255/255, txload 1/255, rxload 1/255
...
Below is the routing table after configuring the router.
Router>show ip route
Codes: L - local, C - connected, S - static, R - RIP, M - mobile, B - BGP
D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area
N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2
E1 - OSPF external type 1, E2 - OSPF external type 2
i - IS-IS, su - IS-IS summary, L1 - IS-IS level-1, L2 - IS-IS level-2
ia - IS-IS inter area, * - candidate default, U - per-user static route
o - ODR, P - periodic downloaded static route, H - NHRP, l - LISP
+ - replicated route, % - next hop override
Gateway of last resort is not set
192.168.1.0/24 is variably subnetted, 2 subnets, 2 masks
C 192.168.1.0/24 is directly connected, Vlan10
L 192.168.1.1/32 is directly connected, Vlan10
192.168.2.0/24 is variably subnetted, 2 subnets, 2 masks
C 192.168.2.0/24 is directly connected, Vlan20
L 192.168.2.1/32 is directly connected, Vlan20
Now we are ready to ping the PC on the other VLAN connected by the router! Here is the result pinging from the Windows PC(192.168.1.2/24) to Raspberry Pi(192.168.2.2/24)
C:\Users\Ryo>ping 192.168.2.2
Pinging 192.168.2.2 with 32 bytes of data:
Reply from 192.168.2.2: bytes=32 time=1ms TTL=63
Reply from 192.168.2.2: bytes=32 time=11ms TTL=63
Reply from 192.168.2.2: bytes=32 time=1ms TTL=63
Reply from 192.168.2.2: bytes=32 time=1ms TTL=63
Ping statistics for 192.168.2.2:
Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 1ms, Maximum = 11ms, Average = 3ms
Configuring the router was somewhat difficult as the commands I found online for inter VLAN routing didn't work for the router(800). I had to do some researching to find the commands that works for the router but it was fun as you don't get to do this kind of researching when you are working on packet tracer😎.
I recently bought a router and 2 switches(L2 and L3) for a studying purpose. Today I tried one of the switches, Catalyst 2960. I connected two of my PC and see if I can ping from one to the other. After connecting two of my PC to the ports Fa0/1-2, I checked if they are really connected.
Switch>show mac address-table
Mac Address Table
-------------------------------------------
Vlan Mac Address Type Ports
---- ----------- -------- -----
All 0100.0ccc.cccc STATIC CPU
All 0100.0ccc.cccd STATIC CPU
All 0180.c200.0000 STATIC CPU
All 0180.c200.0001 STATIC CPU
All 0180.c200.0002 STATIC CPU
All 0180.c200.0003 STATIC CPU
All 0180.c200.0004 STATIC CPU
All 0180.c200.0005 STATIC CPU
All 0180.c200.0006 STATIC CPU
All 0180.c200.0007 STATIC CPU
All 0180.c200.0008 STATIC CPU
All 0180.c200.0009 STATIC CPU
All 0180.c200.000a STATIC CPU
All 0180.c200.000b STATIC CPU
All 0180.c200.000c STATIC CPU
All 0180.c200.000d STATIC CPU
All 0180.c200.000e STATIC CPU
All 0180.c200.000f STATIC CPU
All 0180.c200.0010 STATIC CPU
All ffff.ffff.ffff STATIC CPU
1 902e.169d.b2f1 DYNAMIC Fa0/1
1 e45f.01d3.5bd9 DYNAMIC Fa0/2
Total Mac Addresses for this criterion: 22
You can see the mac address table has the mac address of the each device connected to the port(Fa0/1-2). However, I wasn't able to ping from my Windows PC(Fa0/1) to Raspberry-pi(Fa0/2). I checked the ARP table of my Windows PC and it showed the list with the mac address of raspberry pi.
C:\>arp -a
Interface: 192.168.1.2 --- 0x11
Internet Address Physical Address Type
192.168.1.3 e4-5f-01-d3-5b-d9 dynamic
192.168.1.255 ff-ff-ff-ff-ff-ff static
The problem was my Windows PC's firewall😅. After configuring its firewall rules to allow ICMP traffic, I was able to ping!
C:\Users\Ryo>ping 192.168.1.3
Pinging 192.168.1.3 with 32 bytes of data:
Reply from 192.168.1.3: bytes=32 time=2ms TTL=64
Reply from 192.168.1.3: bytes=32 time=6ms TTL=64
Reply from 192.168.1.3: bytes=32 time=1ms TTL=64
Reply from 192.168.1.3: bytes=32 time<1ms TTL=64
Ping statistics for 192.168.1.3:
Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 0ms, Maximum = 6ms, Average = 2ms
I tried to understand the difference between VLAN and subnet by looking into their ARP broadcast request. First I deleted all the ARP entries on the source PC so that when I ping, there will be an ARP request broadcasted to all the devices connected to the switch. Here is how you can check and delete the ARP table.
C:\>arp -a
Internet Address Physical Address Type
192.168.2.4 0001.436a.8be5 dynamic
C:\>arp -d
C:\>arp -a
No ARP Entries Found
After deleting all the ARP entries, the source PC will send an ARP request as it has no mac address on its table. Here is the list of ports on the switch when an ARP request was sent. It shows an ARP reqeust is sent to all the ports except the one(FastEthernet0/3) which is connected to the source PC. In addition, even though FastEthernet0/1-2 are not within the same subnet(192.168.2.0/24), you can see that they still receive the ARP request sent by the source PC(192.168.2.2/24).
1. FastEthernet0/1 sends out the frame. #192.168.1.2
2. FastEthernet0/2 sends out the frame. #192.168.1.3
3. FastEthernet0/4 sends out the frame. #192.168.2.3
4. FastEthernet0/5 sends out the frame. #192.168.2.4
Now its time see how an ARP request will behave when the switch is divided into VLANs(192.168.1.0/24, 192.168.2.0/24) . I once again deleted all the ARP entries in the table of the source PC and here is the commands I used for creating VLANs.
Switch>enable
Switch#config terminal
Enter configuration commands, one per line. End with CNTL/Z.
Switch(config)#int fa0/1
Switch(config-if)#exit
Switch(config)#vlan 10
Switch(config-vlan)#vlan 20
Switch(config-vlan)#int fa0/1
Switch(config-if)#switchport mode access
Switch(config-if)#switchport access vlan 10
Switch(config-if)#int fa0/2
Switch(config-if)#switchport mode access
Switch(config-if)#switchport access vlan 10
Switch(config-if)#int fa0/3
Switch(config-if)#switchport mode access
Switch(config-if)#switchport access vlan 20
Switch(config-if)#int fa0/4
Switch(config-if)#switchport mode access
Switch(config-if)#switchport access vlan 20
Switch(config-if)#int fa0/5
Switch(config-if)#switchport mode access
Switch(config-if)#switchport access vlan 20
Switch(config-if)#do show vlan
VLAN Name Status Ports
---- -------------------------------- --------- -------------------------------
1 default active Fa0/6, Fa0/7, Fa0/8, Fa0/9
Fa0/10, Fa0/11, Fa0/12, Fa0/13
Fa0/14, Fa0/15, Fa0/16, Fa0/17
Fa0/18, Fa0/19, Fa0/20, Fa0/21
Fa0/22, Fa0/23, Fa0/24, Gig0/1
Gig0/2
10 vlan01 active Fa0/1, Fa0/2
20 vlan02 active Fa0/3, Fa0/4, Fa0/5
1002 fddi-default active
1003 token-ring-default active
1004 fddinet-default active
1005 trnet-default active
I created VLAN01 and VLAN02 for 192.168.1.0/24 and 192.168.2.0/24, respectively. And here is the list of the ports that received an ARP request sent by the source PC(192.168.2.2/24). It shows that FastEthernet0/1-2 are not receiving an ARP request any more as they no longer belong to the same VLAN as the source PC(VLAN02)!
1. FastEthernet0/4 sends out the frame. #VLAN01
2. FastEthernet0/5 sends out the frame. #VLAN02
Once you are done with the VLANs, you can place the ports back to the default VLAN by the below commands.
Switch(config-if-range)#interface range Fa 0/1-5
Switch(config-if-range)#switchport access vlan 1
Switch(config-if-range)#do show vlan
VLAN Name Status Ports
---- -------------------------------- --------- -------------------------------
1 default active Fa0/1, Fa0/2, Fa0/3, Fa0/4
Fa0/5, Fa0/6, Fa0/7, Fa0/8
Fa0/9, Fa0/10, Fa0/11, Fa0/12
Fa0/13, Fa0/14, Fa0/15, Fa0/16
Fa0/17, Fa0/18, Fa0/19, Fa0/20
Fa0/21, Fa0/22, Fa0/23, Fa0/24
Gig0/1, Gig0/2
10 vlan01 active
20 vlan02 active
1002 fddi-default active
1003 token-ring-default active
1004 fddinet-default active
1005 trnet-default active
Switch(config-if-range)#no vlan 10
Switch(config-if-range)#no vlan 20
After studying how different devices work at different layers, I got little confused how ping works if its only given ip address to reach the destination pc connected via switch which is a layer 2 device. Here's how it works.
1. We only have the destination's ip and no mac address info.
->ARP request which is broadcasted to all devices connected to the switch.
2. Once your PC has the mac address in its mac address table, the frame is sent to the switch which contains the mac address of the destination pc.
3. Switch needs to know which port is connected to the PC with the destination mac address.
->Flood the frame to all other ports if the destination mac address is not in the mac address table.
4. Once the switch gets the reply form the destination PC, it now knows which port to send the frame to get to the destination PC!
Here are ARP and Mac address tables before and after ping.
Switch#show mac address-table
Mac Address Table
-------------------------------------------
Vlan Mac Address Type Ports
---- ----------- -------- -----
Switch#show mac address-table
Mac Address Table
-------------------------------------------
Vlan Mac Address Type Ports
---- ----------- -------- -----
1 00e0.8fe3.e0b3 DYNAMIC Fa0/2
1 00e0.f7a1.365d DYNAMIC Fa0/1
C:\>arp -a
No ARP Entries Found
C:\>arp -a
Internet Address Physical Address Type
192.168.1.2 00e0.8fe3.e0b3 dynamic
For testing static routes, I created a system with 2 routers each connected to a PC. There are 3 networks so that one PC can connect to the other after setting up a static route.
Here I first configured router's interface for 192.168.1.0/24 and 192.168.1.1/24 is the default gateway for 192.168.1.0/24. I did the same for the other router responsible for 192.168.2.0/24. The third network(192.168.3.0/24) which needs to be configured is the one connecting the two routers.
Router>enable
Router#configure terminal
Enter configuration commands, one per line. End with CNTL/Z.
Router(config)#interface GigabitEthernet 0/0/0
Router(config-if)#ip address 192.168.1.1 255.255.255.0
Router(config-if)#no shutdown
Router(config-if)#
%LINK-5-CHANGED: Interface GigabitEthernet0/0/0, changed state to up
%LINEPROTO-5-UPDOWN: Line protocol on Interface GigabitEthernet0/0/0, changed state to up
Router(config-if)#do show interfaces
GigabitEthernet0/0/0 is up, line protocol is up (connected)
Hardware is ISR4331-3x1GE, address is 0001.4384.9c01 (bia 0001.4384.9c01)
Internet address is 192.168.1.1/24
...
After setting up all three networks, I pinged form the PC with 192.168.1.2/24. But the result shows that it is still not reachable.
C:\>ping 192.168.2.2
Pinging 192.168.2.2 with 32 bytes of data:
Reply from 192.168.1.1: Destination host unreachable.
Reply from 192.168.1.1: Destination host unreachable.
Reply from 192.168.1.1: Destination host unreachable.
Reply from 192.168.1.1: Destination host unreachable.
Ping statistics for 192.168.2.2:
Packets: Sent = 4, Received = 0, Lost = 4 (100% loss),
The remaining task is to set up the static routes so that the packet can reach the PC with 192.168.2.2/24. Here is how I set up the static route for 192.168.2.2/24.
Router(config)#ip route 192.168.2.0 255.255.255.0 192.168.3.2
After setting up as above, you still won't be able to get replies from the other end as the returning packets get lost at the 192.168.3.2/24.
C:\>ping 192.168.2.2
Pinging 192.168.2.2 with 32 bytes of data:
Request timed out.
Request timed out.
Request timed out.
Request timed out.
Ping statistics for 192.168.2.2:
Packets: Sent = 4, Received = 0, Lost = 4 (100% loss),
Once I set up the static route as below, I was able to get replies from 192.168.2.2/24!
Router(config)#ip route 192.168.1.0 255.255.255.0 192.168.3.1
C:\>ping 192.168.2.2
Pinging 192.168.2.2 with 32 bytes of data:
Reply from 192.168.2.2: bytes=32 time<1ms TTL=126
Reply from 192.168.2.2: bytes=32 time=10ms TTL=126
Reply from 192.168.2.2: bytes=32 time<1ms TTL=126
Reply from 192.168.2.2: bytes=32 time=11ms TTL=126
Ping statistics for 192.168.2.2:
Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 0ms, Maximum = 11ms, Average = 5ms
I have been studying basic networking by reading books for CCNA. At this point, I think I should go hands-on with packet tracer or the router (Cisco 892) and switches (Cisco L2 and L3) I bought this week for studying purpose. Here are the list of the things I want to try hands-on. ・Static route ・OSPF ・ACL ・DHCP ・Unidirection & Bidirectional NAT ・NAPT(PAT) ・VLAN only using access ports (single switch) ・VLAN using access and trunk ports (two switches) ・VLAN rounting (using router and L2 switch, later using L3 switch)
I started using packet tracer and enjoying the tool a lot. I configured hub, switch, and router based network using the tool. It's really fun to put the materials you learned from books into practice! Here are the commands I used for configuring the router and the details of what it's doing.
Router>enable
Router#
Router#configure terminal
Enter configuration commands, one per line. End with CNTL/Z.
Router(config)#interface GigabitEthernet0/0 #select the port(interface) on the router
Router(config-if)#ip address 192.168.1.4 255.255.255.0 #assign gateway IP address to the interface
Router(config-if)#no shutdown #bring up the inerface
Router(config-if)#
%LINK-5-CHANGED: Interface GigabitEthernet0/0, changed state to up
Here I tested if I am connected to the network configured above by pinging from a node within another network.
C:\>ping 192.168.1.3
Pinging 192.168.1.3 with 32 bytes of data:
Request timed out.
Reply from 192.168.1.3: bytes=32 time=14ms TTL=127
Reply from 192.168.1.3: bytes=32 time<1ms TTL=127
Reply from 192.168.1.3: bytes=32 time=11ms TTL=127
Ping statistics for 192.168.1.3:
Packets: Sent = 4, Received = 3, Lost = 1 (25% loss),
Approximate round trip times in milli-seconds:
Minimum = 0ms, Maximum = 14ms, Average = 8ms
It shows that the first packet is dropped. This is because of the switch on the network configured above does not have not have the destination pc's MAC address in its ARP table and need to get the one by 'flooding' which takes some time.
"ip addr" shows the mac address for each interface, it's e4:5f:01:d3:5b:da in the case of wlan0. "arp" shows the router's mac address.
┌──(kali㉿kali-raspberry-pi)-[/var/lib/dhcp]
└─$ ip addr
...
3: wlan0:
The first hop of "traceroute", which is 192.168.164.146, shows the router's local ip address.
┌──(kali㉿kali-raspberry-pi)-[~]
└─$ traceroute 8.8.8.8
traceroute to 8.8.8.8 (8.8.8.8), 30 hops max, 60 byte packets
1 192.168.164.146 (192.168.164.146) 40.242 ms 41.001 ms 43.085 ms
2 121.83.171.60 (121.83.171.60) 74.412 ms 74.476 ms 76.689 ms
3 100.64.3.181 (100.64.3.181) 81.472 ms 81.503 ms 83.573 ms
4 58.191.128.178 (58.191.128.178) 86.298 ms 85.972 ms 86.346 ms
5 61.205.127.233 (61.205.127.233) 86.192 ms 87.454 ms 87.482 ms
6 203.140.81.209 (203.140.81.209) 86.012 ms 48.984 ms 55.241 ms
7 60.56.20.190 (60.56.20.190) 54.917 ms 54.165 ms 62.331 ms
8 142.250.172.36 (142.250.172.36) 62.152 ms 61.755 ms 65.046 ms
9 * * *
10 dns.google (8.8.8.8) 65.708 ms 65.737 ms 65.926 ms
After piging to my laptop within the same local network, my laptop's mac address is added to the arp table. But ping is failing for some reason...
┌──(kali㉿kali-raspberry-pi)-[/var/lib/dhcp]
└─$ ping 192.168.164.137
PING 192.168.164.137 (192.168.164.137) 56(84) bytes of data.
^C
--- 192.168.164.137 ping statistics ---
7 packets transmitted, 0 received, 100% packet loss, time 6143ms
┌──(kali㉿kali-raspberry-pi)-[/var/lib/dhcp]
└─$ arp
Address HWtype HWaddress Flags Mask Iface
192.168.164.146 ether ac:a8:8e:ec:6e:66 C wlan0
192.168.164.137 ether 50:c2:e8:29:9b:cb C wlan0
Wireshark shows the arp protocol is requesting the mac address of 192.168.164.137, and the response from the router shows the mac address is 50:c2:e8:29:9b:cb. However there is no response from 192.168.164.76.
2 18.664379309 Raspberr_d3:5b:da Broadcast ARP 42 Who has 192.168.164.137? Tell 192.168.164.76
3 18.680115301 CloudNet_29:9b:cb Raspberr_d3:5b:da ARP 42 192.168.164.137 is at 50:c2:e8:29:9b:cb
4 18.680196856 192.168.164.76 192.168.164.137 ICMP 98 Echo (ping) request id=0x0002, seq=1/256, ttl=64 (no response found!)
I checked my laptop's firewall configuration and enable ping echo request. Ping is now working!
┌──(kali㉿kali-raspberry-pi)-[~]
└─$ ping 192.168.164.137
PING 192.168.164.137 (192.168.164.137) 56(84) bytes of data.
64 bytes from 192.168.164.137: icmp_seq=1 ttl=128 time=53.2 ms
64 bytes from 192.168.164.137: icmp_seq=2 ttl=128 time=75.9 ms
64 bytes from 192.168.164.137: icmp_seq=3 ttl=128 time=8.42 ms
64 bytes from 192.168.164.137: icmp_seq=4 ttl=128 time=42.1 ms
64 bytes from 192.168.164.137: icmp_seq=5 ttl=128 time=8.71 ms
^C
--- 192.168.164.137 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4006ms
rtt min/avg/max/mdev = 8.417/37.644/75.852/26.122 ms
Here are the commands I used after finishing compiling the source code.
──(kali㉿kali-raspberry-pi)-[~/sources/linux]
└─$ ls
arch certs CREDITS Documentation fs init ipc Kconfig lib MAINTAINERS mm modules.builtin.modinfo modules.order net README.md scripts sound tools virt vmlinux.o
block COPYING crypto drivers include io_uring Kbuild kernel LICENSES Makefile modules.builtin modules-only.symvers Module.symvers README samples security System.map usr vmlinux vmlinux.symvers
┌──(kali㉿kali-raspberry-pi)-[~/sources/linux]
└─$ sudo make modules_install
┌──(kali㉿kali-raspberry-pi)-[~/sources/linux]
└─$ sudo cp arch/arm64/boot/dts/broadcom/*.dtb /boot/
┌──(kali㉿kali-raspberry-pi)-[~/sources/linux]
└─$ sudo cp arch/arm64/boot/dts/overlays/*.dtb* /boot/overlays/
┌──(kali㉿kali-raspberry-pi)-[~/sources/linux]
└─$ sudo cp arch/arm64/boot/dts/overlays/README /boot/overlays/
┌──(kali㉿kali-raspberry-pi)-[~/sources/linux]
└─$ sudo cp arch/arm64/boot/Image.gz /boot/kernel-ryo-1.img
┌──(kali㉿kali-raspberry-pi)-[/boot]
└─$ sudo vim config.txt
# 64-bit kernel for Raspberry Pi 4 is called kernel8l (armv8a)
kernel=kernel-ryo-1.img
┌──(kali㉿kali-raspberry-pi)-[/boot]
└─$ sudo reboot
And I got this!
┌──(kali㉿kali-raspberry-pi)-[/var/log]
└─$ uname -a
Linux kali-raspberry-pi 5.15.83-v8-ryos-first-kernel+ #1 SMP PREEMPT Fri Dec 16 12:25:46 UTC 2022 aarch64 GNU/Linux
┌──(kali㉿kali-raspberry-pi)-[/var/log]
└─$ cat messages
Dec 16 14:43:01 kali-raspberry-pi pulseaudio[968]: ICE I/O error handler called
Dec 16 14:43:04 kali-raspberry-pi kernel: [ 0.000000] Hello World, I'm Ryo!
Dec 16 14:43:04 kali-raspberry-pi kernel: [ 0.000000] Booting Linux on physical CPU 0x0000000000 [0x410fd083]
Dec 16 14:43:04 kali-raspberry-pi kernel: [ 0.000000] Linux version 5.15.83-v8-ryos-first-kernel+ (kali@kali-raspberry-pi) (gcc (Debian 11.3.0-5) 11.3.0, GNU ld (GNU Binutils for Debian) 2.38.90.20220713) #1 SMP PREEMPT Fri Dec 16 12:25:46 UTC 2022
This time I was able to print during the boot up with my customized kernel! I can now try to do some work on the network stack and compile😁.
This time, I modified /arm/arm64/kernel/setup.c as below. You can find the source code here.
void __init smp_setup_processor_id(void)
{
u64 mpidr = read_cpuid_mpidr() & MPIDR_HWID_BITMASK;
set_cpu_logical_map(0, mpidr);
pr_info("Hello World, its Ryo!");
pr_info("Booting Linux on physical CPU 0x%010lx [0x%08x]\n",
(unsigned long)mpidr, read_cpuid_id());
}
Here is the commands that I used.
$ KERNEL=kernel8
$ make bcm2711_defconfig
$ vim .config
CONFIG_LOCALVERSION="-v8-ryos-first-kernel"
$ make -j4 Image.gz modules dtbs
It's completed compiling! Here is what I did afterwards.
$ make
...
OBJCOPY arch/arm64/boot/Image
GZIP arch/arm64/boot/Image.gz
[1] + done make
$ ls
arch certs CREDITS Documentation fs init Kbuild kernel LICENSES Makefile modules.builtin modules-only.symvers Module.symvers README scripts sound tools virt vmlinux.o
block COPYING crypto drivers include ipc Kconfig lib MAINTAINERS mm modules.builtin.modinfo modules.order net samples security System.map usr vmlinux vmlinux.symvers
$ file vmlinux
vmlinux: ELF 64-bit LSB pie executable, ARM aarch64, version 1 (SYSV), statically linked, BuildID[sha1]=8d74859bebe9cce14587e1b3272c7b11fe5de611, not stripped
$ sudo make modules_install
INSTALL /lib/modules/5.15.44-Re4son-v8l/kernel/sound/usb/misc/snd-ua101.ko
INSTALL /lib/modules/5.15.44-Re4son-v8l/kernel/sound/usb/snd-usb-audio.ko
INSTALL /lib/modules/5.15.44-Re4son-v8l/kernel/sound/usb/snd-usbmidi-lib.ko
DEPMOD /lib/modules/5.15.44-Re4son-v8l
$ sudo make install
sh ./arch/arm64/boot/install.sh 5.15.44-Re4son-v8l \
arch/arm64/boot/Image System.map "/boot"
run-parts: executing /etc/kernel/postinst.d/initramfs-tools 5.15.44-Re4son-v8l /boot/vmlinuz-5.15.44-Re4son-v8l
update-initramfs: Generating /boot/initrd.img-5.15.44-Re4son-v8l
cryptsetup: ERROR: Couldn't resolve device /dev/root
cryptsetup: WARNING: Couldn't determine root device
I'm having trouble installing compiled files. I did some research and boot up process is not what I thought as Rasbarry Pi does not support grub😰. I found the kernel source code for Raspberry Pi here. Another mistake that I found is that I was working on linux/arch/arm. My Raspberry Pi is 64bits machine so the directory should be under linux/arch/arm64. Glad I found this mistake before compilation.
I will once again compile the source code and see how it goes😂.
Some stuff I learned about how Nmap SYN scanning works. Below is the captured packets from Wireshark.
10 13.006851 192.168.100.103 192.168.100.101 TCP 60 44276 → 445 [SYN] Seq=0 Win=1024 Len=0 MSS=1460
15 13.007423 192.168.100.101 192.168.100.103 TCP 60 445 → 44276 [SYN, ACK] Seq=0 Ack=1 Win=65535 Len=0 MSS=1460
16 13.007594 192.168.100.103 192.168.100.101 TCP 60 44276 → 445 [RST] Seq=1 Win=0 Len=0
It shows that the the state of port is open as the server's reply to the nmap's SYN is SYN,ACK. You can observe that SYN scan terminates 3 way handshake by RST sent by nmap. Also, here is the source code I found which I thought might be useful for spoofing Nmap OS detection. You can find the source code here. I think the below code is the part where it specifies the window size of a packet.
/* Numbers are taken from RFC3390.
*
* John Heffner states:
*
* The RFC specifies a window of no more than 4380 bytes
* unless 2*MSS > 4380. Reading the pseudocode in the RFC
* is a bit misleading because they use a clamp at 4380 bytes
* rather than use a multiplier in the relevant range.
*/
__u32 tcp_init_cwnd(struct tcp_sock *tp, struct dst_entry *dst)
{
__u32 cwnd = (dst ? dst_metric(dst, RTAX_INITCWND) : 0);
if (!cwnd) {
if (tp->mss_cache > 1460)
cwnd = 2;
else
cwnd = (tp->mss_cache > 1095) ? 3 : 4;
}
return min_t(__u32, cwnd, tp->snd_cwnd_clamp);
}
I modified ./arch/arm/kernel/setup.c as below. You can find the source code here.
void __init smp_setup_processor_id(void)
{
int i;
u32 mpidr = is_smp() ? read_cpuid_mpidr() & MPIDR_HWID_BITMASK : 0;
u32 cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
cpu_logical_map(0) = cpu;
for (i = 1; i < nr_cpu_ids; ++i)
cpu_logical_map(i) = i == cpu ? 0 : i;
/*
* clear __my_cpu_offset on boot CPU to avoid hang caused by
* using percpu variable early, for example, lockdep will
* access percpu variable inside lock_release
*/
set_my_cpu_offset(0);
pr_info("Hello world, this is Ryo speaking!\n");
pr_info("Booting Linux on physical CPU 0x%x\n", mpidr);
}
After the above modification, I started compiling the source code as below.
$ cp /boot/config-$(uname -r) .config
$ sudo apt-get install git fakeroot build-essential ncurses-dev xz-utils libssl-dev bc flex libelf-dev bison
$ make menuconfig
No change to .config
*** End of the configuration.
*** Execute 'make' to start the build or try 'make help'.
$ make
This is going to take a while😝.
I learned that you can't use printk() during the early stage of the boot up process😅. Here is the output of /var/log/messages.
┌──(kali㉿kali-raspberry-pi)-[/var/log]
└─$ cat messages
Dec 11 02:00:25 kali-raspberry-pi rsyslogd: [origin software="rsyslogd" swVersion="8.2206.0" x-pid="525" x-info="https://www.rsyslog.com"] rsyslogd was HUPed
Dec 11 02:27:19 kali-raspberry-pi kernel: [ 0.000000] Booting Linux on physical CPU 0x0000000000 [0x410fd083]
Dec 11 02:27:19 kali-raspberry-pi kernel: [ 0.000000] Linux version 5.15.44-Re4son-v8l+ (root@kali-raspberry-pi) (gcc (Debian 11.2.0-19) 11.2.0, GNU ld (GNU Binutils for Debian) 2.38) #1 SMP PREEMPT Debian kali-pi (2022-07-03)
Dec 11 02:27:19 kali-raspberry-pi kernel: [ 0.000000] random: crng init done
Dec 11 02:27:19 kali-raspberry-pi kernel: [ 0.000000] Machine model: Raspberry Pi 4 Model B Rev 1.5
I once again gone through the source code and found that "Booting Linux on" is printed by pr_info() and Linux version 5.15.44-Re4son-v8l+ is printed by decompressor_printk(). I will go for pr_info() as it comes earlier than decompressor_printk()😝.
┌──(kali㉿kali-raspberry-pi)-[~/sources/linux-5.15.44]
└─$ grep -r "Booting" .
./arch/arm/kernel/setup.c: pr_info("Booting Linux on physical CPU 0x%x\n", mpidr);
┌──(kali㉿kali-raspberry-pi)-[~/sources/linux-5.15.44]
└─$ grep printk * -r | grep "decompressor_printk"
arch/s390/boot/pgm_check_info.c: decompressor_printk("Linux version %s\n", kernel_version);
I check my Raspberry Pi kernel version and its 5.15.44.
$ uname -r
5.15.44-Re4son-v8l+
I want to printk during the boot up process so I look through the kernel source code where "Booting Linux on..." is printed as shown below.
┌──(kali㉿kali-raspberry-pi)-[/var/log]
└─$ cat messages
Dec 11 02:00:25 kali-raspberry-pi rsyslogd: [origin software="rsyslogd" swVersion="8.2206.0" x-pid="525" x-info="https://www.rsyslog.com"] rsyslogd was HUPed
Dec 11 02:27:19 kali-raspberry-pi kernel: [ 0.000000] Booting Linux on physical CPU 0x0000000000 [0x410fd083]
Dec 11 02:27:19 kali-raspberry-pi kernel: [ 0.000000] Linux version 5.15.44-Re4son-v8l+ (root@kali-raspberry-pi) (gcc (Debian 11.2.0-19) 11.2.0, GNU ld (GNU Binutils for Debian) 2.38) #1 SMP PREEMPT Debian kali-pi (2022-07-03)
Dec 11 02:27:19 kali-raspberry-pi kernel: [ 0.000000] random: crng init done
Dec 11 02:27:19 kali-raspberry-pi kernel: [ 0.000000] Machine model: Raspberry Pi 4 Model B Rev 1.5
Seems like "Booting Linux on..." is printed by pr_info(), not printk(). Not yet sure where the first printk() is...
You can find the source code for version 5.15.44 here.
Today I inserted a kernel module which simply prints "Hello Ryo!" under /var/log/messages. Here is the c program.
int init_module(void)
{
//printk(KERN_INFO "Hello world 1.\n");
printk(KERN_INFO "Hello Ryo!\n");
return 0;
}
void cleanup_module(void)
{
//printk(KERN_INFO "Goodbye world 1.\n");
printk(KERN_INFO "Bye Ryo!\n");
}
MODULE_LICENSE("GPL");
initi_module() is used to initialize the module and cleanup_module is responsible for shutting down the module. When you are using printk(), you can give eight 8 levels of different logs at the part of "KERN_INFO". Below is the commands I used for initializing and removing the kernel.
make -C /lib/modules/`uname -r`/build/ M=${PWD} modules
sudo insmod hello_world.ko
sudo rmmod hello_world.ko
Here is the output of the above operation.
Dec 12 14:21:27 kali-raspberry-pi kernel: [ 8233.939103] Hello Ryo!
Dec 12 14:22:33 kali-raspberry-pi kernel: [ 8299.742310] Bye Ryo!
I will start looking into where I can printk() inside the kernel source code during the boot up process!
I've been working on TUN device for a while and thought I should look into how packets are handled within the kernel. TUN device allows us to handle packets above layer 3. The corresponding entry point within the kernel is ip_rcv() whih can be found here. ip_local_deliver_finish() is the function whichis the exit of the layer 3 responsible for passing data to the layer 4 stack based on the protocol found in the ip header. You can find the function https://elixir.bootlin.com/linux/v2.6.20/source/net/ipv4/ip_input.c#L199. After ip_local_deliver_finish(), the data is sent to the function handling layer 4 protocol such as tcp_v4_rcv(), udp_rcv(), icmp_rcv(). Might be fun to customize your own network stack and make a kernel of your own😝.
I am getting used to reading the hexs shown in Wireshark. Here is the summary of what is going with the below packet sent by my TUN program.
0x00, 0x00, 0x08, 0x00, 0x45, 0x00, 0x00, 0x54, 0x89, 0x4b, 0x40, 0x00, 0x40, 0x01, 0x39, 0x59, 0xcb, 0x00, 0x71, 0x02, 0xcb, 0x00, 0x71, 0x01, 0x08, 0x00, 0x2a, 0xc7, 0x00, 0x19, 0x00, 0x07, 0xc8, 0xb6, 0x8c, 0x63, 0x00, 0x00, 0x00, 0x00, 0xaf, 0x2b, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37
The first 4 bit (4 of 0x45)
・Version: the fist field tells us which IP version we are using, only IPv4 uses this header so you will always find decimal value 4 here.
The first 4 bit (5 of 0x45)
・The length of the ip header. In the case of this example, 5=>5*4(bytes)=>20(bytes)=>20 pair(0x45)s, as each hex is 4 bits.
・Because of the maximum possible value f=>15, the maximum ip header length is 15*4byte=60byte.
Protocol: 0x01
・1 stands for ICMP, 6 stands for tcp, 17 stands for UDP
Source and destination address: 0xcb, 0x00, 0x71, 0x02, 0xcb, 0x00, 0x71, 0x01
・In decimal, 203.0.113.2 and 203.0.113.1 respectively.
Below is the c program for sending receiving packets using TUN device. You should comment out either the dump_packet() or send_packet(). Otherwise, the program will be sending and receiving packets of its own.
void dump_packet(int fd, int count, char* buffer)
{
printf("the fd is %d. read %d bytes!\n", fd, count);
int i;
for (i=0; i
Wireshark shows the packet are exchanged as below. It's working!
1 0.000000000 203.0.113.2 203.0.113.1 ICMP 86 Echo (ping) request id=0x0019, seq=7/1792, ttl=64 (reply in 2)
2 0.000082333 203.0.113.1 203.0.113.2 ICMP 84 Echo (ping) reply id=0x0019, seq=7/1792, ttl=64 (request in 1)
Below is the database file which Nmap uses as a reference to packet info specific to OS. It is at /usr/share/nmap/nmap-os-db in the case of Ubuntu 20.04.4 LTS.
# Linux 2.6.30-ARCH #1 SMP PREEMPT Wed Sep 9 12:37:32 UTC 2009 i686 AMD Athlon(tm) XP 2000+ AuthenticAMD GNU/Linux
Fingerprint Linux 2.6.30
Class Linux | Linux | 2.6.X | general purpose
CPE cpe:/o:linux:linux_kernel:2.6.30
SEQ(SP=C1-CB%GCD=1-6%ISR=C5-CF%TI=Z%II=I%TS=U)
OPS(O1=M5B4NNSNW6%O2=M5B4NNSNW6%O3=M5B4NW6%O4=M5B4NNSNW6%O5=M5B4NNSNW6%O6=M5B4NNS)
WIN(W1=16D0%W2=16D0%W3=16D0%W4=16D0%W5=16D0%W6=16D0)
ECN(R=Y%DF=Y%T=3B-45%TG=40%W=16D0%O=M5B4NNSNW6%CC=N%Q=)
T1(R=Y%DF=Y%T=3B-45%TG=40%S=O%A=S+%F=AS%RD=0%Q=)
T2(R=N)
T3(R=Y%DF=Y%T=3B-45%TG=40%W=16D0%S=O%A=S+%F=AS%O=M5B4NNSNW6%RD=0%Q=)
T4(R=N)
T5(R=Y%DF=Y%T=3B-45%TG=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)
T6(R=N)
T7(R=Y%DF=Y%T=3B-45%TG=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)
U1(DF=N%T=3B-45%TG=40%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=G%RUD=G)
IE(DFI=N%T=3B-45%TG=40%CD=S)
The Nmap source code can be found here.
I'm curious how Nmap OS detection works. Project like below is something that I am interested! https://nmap.org/misc/defeat-nmap-osdetect.html
I added the below code to the previous post to receive ICMP packets.
char *buffer = (char *) calloc(150, sizeof(char));
ssize_t count = read(fd, buffer, 100);
Below is how read() can be used.
The read() function shall attempt to read nbyte bytes from the file associated with the open file descriptor, fildes, into the buffer pointed to by buf.
In the absence of errors, or if error detection is not performed, the read() function shall return zero and have no other results. reference
Below is the received packet in hex.
00 00 08 00 45 00 00 54 89 4b 40 00 40 01 39 59 cb 00 71 01 cb 00 71 02 08 00 2a c7 00 19 00 07 c8 b6 8c 63 00 00 00 00 af 2b 0a 00 00 00 00 00 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37
The above shows that the source address is "cb 00 71 01->203.0.113.1" and the destination address is "cb 00 71 02->203.0.113.290". I'll try to send packets next time! I'm curious simply switching the source and destination address will be enough to send back the packet?
I created a TUN interface with the below c program.
int main(int argc, char** argv)
{
if (argc != 2)
return 1;
const char* device_name = argv[1];
if (strlen(device_name) + 1 > IFNAMSIZ)
{
return 1;
}
int fd = open("/dev/net/tun", O_RDWR);
if (fd == -1)
{
return 1;
}
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TUN;
strncpy(ifr.ifr_name, device_name, IFNAMSIZ);
int res = ioctl(fd, TUNSETIFF, &ifr);
if (res == -1)
{
return 1;
}
printf("the created device is only available during this program\n");
while (1) {
}
return 0;
}
The important part in the above code is below. You can provide the name of the interface to "ifr.ifr_name" and spcify whether you want the interface to be TUN or TAP with "ifr.ifr_flags".
int fd = open("/dev/net/tun", O_RDWR);
int res = ioctl(fd, TUNSETIFF, &ifr);
There is another way of creating TUN device using ip command which is shown below.
$ sudo ip tuntap add dev tun0 mode tun
My next attempt is to receive and read packets using read() and send(). I also want to see how packets are being exchanged using Wireshark.
Maybe TUN/TAP is a good place to start understanding how low-level networking works?
You can check the file descriptor and associated device files with the below command.
$ lsof
cat 307 ryo 1w REG 8, 32 0 106248 /home/ryo/ctf/kernel/file1.txt
Above shows that device files have major and minor numbers to identify the associated device drivers. You can see that 8, 32 are the major and minor number in the above case. You can check which device number has 8, 32 by the below command.
/dev$ ls -la
brw------- 1 root root 8, 32 Nov 23 11:51 sdc
Maybe I should start looking into device drivers used for networking?
I used Wireshark to observe how packets are exchanged between client and server. Packets are sent by client form some random port. Packets are received by the server with the specified port by the running program.
Client socket program written in python
import socket
# AF_INET refers to ipv4
# SOCK_STREAM refers to TCP protocol
clientSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 3 way handshake(SYN) as socket.SOCK_STREAM is the selected type
# clientSocket.connect(('127.0.0.1', 12345))
clientSocket.connect(('192.168.50.130', 1234))
dataFromServer = clientSocket.recv(1024)
print(dataFromServer.decode())
Server socket program written in python
import socket
s = socket.socket()
port = 12345
s.bind(('', port))
s.listen(5)
while True:
# 3 way handshake(ACK) as socket.SOCK_STREAM is the selected type
clientSocket, addr = s.accept()
print(addr)
clientSocket.send('Thank you for connecting'.encode())
clientSocket.close()
break
Packets form Wireshark
1 0.000000000 127.0.0.1 127.0.0.1 TCP 74 44070 → 1234 [SYN] Seq=0 Win=65495 Len=0 MSS=65495 SACK_PERM=1 TSval=4210716831 TSecr=0 WS=128
2 0.000113299 127.0.0.1 127.0.0.1 TCP 74 1234 → 44070 [SYN, ACK] Seq=0 Ack=1 Win=65483 Len=0 MSS=65495 SACK_PERM=1 TSval=4210716831 TSecr=4210716831 WS=128
Some stuff I learned about socket programming ・client socket socket.socket, socket.connect ・server socket socket.connect, socket.bind, socket.listen, socket.accept Client socket sends SYN with socket.connect and server socket sends ACK with socket.accept. Server socket create client-like(temporary) socket for every incoming client socket.