2.6.19에서 rtnetlink의 미묘한 변화
Posted by 미스란디르
2.4 커널에서 잘 돌아가던 어플이 하나 있다.
요녀석은 ip관리에 netlink(rtnetlink) 를 사용한다. 어떤식이냐면, 시작할때 control용 netlink소켓 하나, 그리고 broadcasting을 받기위한 listen netlink소켓 하나를 열어둔다. 그리고 ip를 추가하거나 뺄 때 control소켓을 쓴다.
요 소켓을 통해서 RTM_NEWADDR이나 RTM_DELADDR같은 이벤트를 발생시키면 커널이 알아서 잘 해준다. 그리고 ip가 더해지거나 빼지면, 커널은 listen소켓에 broadcasting으로 그 사실을 알려준다.
그리고 요 listen 소켓 핸들러에 이런걸 해놨다. 아이피가 없다가 하나가 더해지거나, 전부 빠지면 ip_exist란 플래그를 켜거나 끄도록 말이다. 그리고 다른데서 요 플래그를 참조해서 뭔가 할지 말지 정한다.
그런데 요게 2.6.19에서 돌리니 갑자기 안돈다. ip_exist를 설정해야되는데, 이게 죽어도 안되는 거다. 신기한건 자기가 직접 추가 안하고 외부에서 iproute2 같은걸로 추가하면 listen소켓이 제대로 반응한다.
니가 이기나 내가 이기나 열심히 뒤져 봤다. 그랬더니 이런 코드가 있었다.
if (nl != &netlink_cmd && h->nlmsg_pid == netlink_cmd.snl.nl_pid)
{
/* control 소켓에서 온건 무시~ */
.
.
}
물론 볼것도 없이 h->nlmsgpid 가 컨트롤소켓이 바인딩된 nlpid 값이다. 그리고 약간 이상한 점이 있는데, 저게 -4097인가 -4098가 하는 이상한 값이다. 세상에 저런 pid가 어디 있겠는가. 뭐 아무튼 그렇다.
여기서 첫번째 의심 - 대체 어떻게 하면 음수의 pid값이 나오나.. 뭔가 잘못된 값이라고 짐작해봤다. 구조체가 살짝 밀렸거나 뭐 그런경우가 아닐까 하고 한참 뚫어져라 봤지만 문제가 전혀 없다. 이건 포기.
그렇다면.. 저걸 무시하면 잘 넘어가나? 그렇다. 저부분을 신경쓰지 않도록 하면 잘 넘어간다. 음 그런데 2.4에선 저걸 저렇게 해도 문제가 없었다. 즉 저 pid값이 컨트롤 소켓 바인딩된 값이랑 분명히 다르게 온거다.
이제 비교시작. nlh에 pid를 채우는 부분을 커널에서 열심히 찾아봤다. 유레카! 2.6.19에서 갑자기 커널이 notify하는 넷링크 패킷의 pid를 발생자 pid로 채우기 시작했다. rtmsg_ifa() 함수가 인자로 pid를 받기 시작한거다. 덕분에 원래는 커널 notify라서 pid 0번으로 오던 저 메시지들이 전부 pid를 달고 오기 시작했다. 해결책은 두가지가 되겠다. 예전처럼 pid 0번으로 바꿔버리던가, control 소켓이 보낸 거라도 무시하지 말고 잘 받아도록 하면 되겠다.
PS. 이거 누가 바꾼건지 Changelog에도 안나와서 고민했는데, git를 뒤진끝에 드디어 로그를 발견했다. 근데 정작 pid 왜추가했는지에 대한 설명은 있지도 않다 -_-;; 사소한거에 집착을 너무 했나보다.
