├── .github
├── codeql
│ └── codeql-config.yml
└── workflows
│ ├── codeql-analysis.yml
│ └── semgrep.yml
├── CreateMockPort.sh
├── Dockerfile
├── Dockerfile.bm.prod
├── Dockerfile.build
├── Dockerfile.common.prod
├── Dockerfile.msee.prod
├── Dockerfile.test
├── LICENSE
├── Makefile
├── README.md
├── arp_responder
├── .gitignore
├── Makefile
├── ToDo
├── arp.cc
├── arp.h
├── arp_responder.thrift
├── arpresponder_bm.cc
├── arpresponder_bm.h
├── arpresponder_bm_main.cc
├── arpresponder_msee.cc
├── arpresponder_msee.h
├── arpresponder_msee_client.cc
├── arpresponder_msee_main.cc
├── cmd.cc
├── cmd.h
├── debian
│ ├── arpresponder-bm.install
│ ├── arpresponder-msee-client.install
│ ├── arpresponder-msee.install
│ ├── changelog
│ ├── compat
│ ├── control
│ └── rules
├── eintr.h
├── fmt.cc
├── fmt.h
├── intf.cc
├── intf.h
├── log.h
├── poller.cc
├── poller.h
└── tests
│ ├── build
│ ├── Dockerfile
│ ├── dep
│ │ └── .gitkeep
│ └── supervisord.conf
│ ├── ptf_tests
│ ├── bm_tests.py
│ └── msee_tests.py
│ └── test.sh
├── arpthrift
├── GoUnusedProtection__.go
├── arp-consts.go
├── arp.go
└── arp_responder-remote
│ └── arp_responder-remote.go
├── arpthrifttest
├── arp.thrift
└── server.go
├── azurepipeline.yml
├── build.md
├── build.sh
├── cert
├── client
│ ├── selfsigned.crt
│ └── selfsigned.key
└── server
│ ├── selfsigned.crt
│ └── selfsigned.key
├── copy.py
├── copy.sh
├── cover.sh
├── debian
├── changelog
├── compat
├── control
├── rules
└── sonic-rest-api.dirs
├── debs
└── .keep
├── debug
├── show_encap_tunnels
├── show_interface
├── show_routes
└── show_vrouters
├── dependencies.conf
├── files
└── server_kill.py
├── go-server-server
├── .swagger-codegen-ignore
├── go.mod
├── go.sum
├── go
│ ├── auth.go
│ ├── default.go
│ ├── flags.go
│ ├── logger.go
│ ├── models.go
│ ├── netstatus.go
│ ├── persistent.go
│ ├── routers.go
│ └── util.go
├── main.go
└── main_test.go
├── libcswsscommon
├── .gitignore
├── Makefile
├── include
│ └── capi
│ │ ├── dbconnector.h
│ │ ├── producerstatetable.h
│ │ ├── producertable.h
│ │ └── table.h
└── src
│ ├── dbconnector.cpp
│ ├── producerstatetable.cpp
│ ├── producertable.cpp
│ └── table.cpp
├── mseethrift
├── GoUnusedProtection__.go
├── m_s_e_e-remote
│ └── m_s_e_e-remote.go
├── msee-consts.go
└── msee.go
├── mseethrifttest
├── Makefile
├── client.go
├── msee.thrift
└── server.go
├── sonic_api.yaml
├── start.sh
├── supervisor
├── arp_mock_server.conf
├── arp_responder_bm.conf
├── arp_responder_msee.conf
├── redis.conf
├── rest_api.conf
├── rest_api_test.conf
├── supervisor.conf
└── thrift_mock_server.conf
├── swsscommon
├── dbconnector.go
├── go.mod
├── producerstatetable.go
└── table.go
├── swsscommontest
└── main.go
├── test.sh
└── test
├── .gitignore
├── apitest.py
├── config
├── arpresponder.conf
└── config.ini
├── conftest.py
├── requirements.txt
├── restapi_client.py
└── test_restapi.py
/.github/codeql/codeql-config.yml:
--------------------------------------------------------------------------------
1 | name: "CodeQL config"
2 | queries:
3 | - uses: security-and-quality
4 | - uses: security-extended
5 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | # For more infomation, please visit: https://github.com/github/codeql-action
2 |
3 | name: "CodeQL"
4 |
5 | on:
6 | push:
7 | branches:
8 | - 'master'
9 | - '202[0-9][0-9][0-9]'
10 | pull_request_target:
11 | branches:
12 | - 'master'
13 | - '202[0-9][0-9][0-9]'
14 |
15 | jobs:
16 | analyze:
17 | name: Analyze
18 | runs-on: ubuntu-latest
19 | permissions:
20 | actions: read
21 | contents: read
22 | security-events: write
23 |
24 | strategy:
25 | fail-fast: false
26 | matrix:
27 | language: [ 'python','go' ]
28 |
29 | steps:
30 | - name: Checkout repository
31 | uses: actions/checkout@v3
32 |
33 | # Initializes the CodeQL tools for scanning.
34 | - name: Initialize CodeQL
35 | uses: github/codeql-action/init@v2
36 | with:
37 | config-file: ./.github/codeql/codeql-config.yml
38 | languages: ${{ matrix.language }}
39 |
40 | - name: Perform CodeQL Analysis
41 | uses: github/codeql-action/analyze@v2
42 | with:
43 | category: "/language:${{matrix.language}}"
44 |
--------------------------------------------------------------------------------
/.github/workflows/semgrep.yml:
--------------------------------------------------------------------------------
1 | name: Semgrep
2 |
3 | on:
4 | pull_request: {}
5 | push:
6 | branches:
7 | - master
8 | - '201[7-9][0-1][0-9]'
9 | - '202[0-9][0-1][0-9]'
10 |
11 | jobs:
12 | semgrep:
13 | if: github.repository_owner == 'sonic-net'
14 | name: Semgrep
15 | runs-on: ubuntu-latest
16 | container:
17 | image: returntocorp/semgrep
18 | steps:
19 | - uses: actions/checkout@v3
20 | - run: semgrep ci
21 | env:
22 | SEMGREP_RULES: p/default
23 |
--------------------------------------------------------------------------------
/CreateMockPort.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | /sbin/brctl addbr Ethernet12
4 | /sbin/ifconfig Ethernet12 up
5 | /sbin/brctl addbr Ethernet16
6 | /sbin/ifconfig Ethernet16 up
7 | /sbin/brctl addbr Ethernet34
8 | /sbin/ifconfig Ethernet34 up
9 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM debian:buster
2 |
3 | ## Make apt-get non-interactive
4 | ENV DEBIAN_FRONTEND=noninteractive
5 |
6 | RUN echo "deb http://archive.debian.org/debian buster-backports main" >> /etc/apt/sources.list
7 |
8 | RUN apt-get update \
9 | && apt-get install -y \
10 | vim \
11 | redis-server \
12 | supervisor \
13 | curl \
14 | bridge-utils \
15 | net-tools \
16 | libboost-serialization1.71-dev \
17 | libzmq5-dev
18 |
19 | COPY debs /debs
20 | RUN dpkg -i /debs/*.deb || apt-get --fix-broken -y install
21 | RUN rm -fr /debs
22 |
23 | COPY supervisor/supervisor.conf /etc/supervisor/conf.d/
24 | COPY supervisor/rest_api.conf /etc/supervisor/conf.d/
25 |
26 | RUN mkdir /usr/sbin/cert
27 | RUN mkdir /usr/sbin/cert/client
28 | RUN mkdir /usr/sbin/cert/server
29 | COPY cert/client/* /usr/sbin/cert/client/
30 | COPY cert/server/* /usr/sbin/cert/server/
31 |
32 | RUN apt-get autoremove -y \
33 | && apt-get clean \
34 | && rm -fr /var/lib/apt/lists/* /tmp/* /var/tmp/*
35 |
36 | ENTRYPOINT ["/usr/bin/supervisord"]
37 |
--------------------------------------------------------------------------------
/Dockerfile.bm.prod:
--------------------------------------------------------------------------------
1 | FROM rest-api-common
2 |
3 | RUN apt-get autoremove -y \
4 | && apt-get clean \
5 | && rm -fr /var/lib/apt/lists/* /tmp/* /var/tmp/*
6 |
7 | COPY supervisor/arp_responder_bm.conf /etc/supervisor/conf.d/
8 | COPY supervisor/rest_api_prod.conf /etc/supervisor/conf.d/
9 |
--------------------------------------------------------------------------------
/Dockerfile.build:
--------------------------------------------------------------------------------
1 | FROM debian:buster
2 |
3 | ## Make apt-get non-interactive
4 | ENV DEBIAN_FRONTEND=noninteractive
5 |
6 | RUN echo "deb http://archive.debian.org/debian buster-backports main" >> /etc/apt/sources.list
7 |
8 | RUN apt-get update \
9 | && apt-get install -y \
10 | wget \
11 | git \
12 | build-essential \
13 | debhelper \
14 | dh-exec \
15 | automake \
16 | bison \
17 | flex \
18 | g++ \
19 | git \
20 | libboost-serialization1.71-dev \
21 | libhiredis-dev \
22 | libzmq5-dev \
23 | libevent-dev \
24 | libssl-dev \
25 | libtool \
26 | make \
27 | sudo \
28 | pkg-config
29 |
30 | COPY debs /debs
31 | RUN dpkg -i /debs/*.deb || apt-get --fix-broken -y install
32 | RUN rm -fr /debs
33 |
34 | #RUN export VERSION=0.9.3 \
35 | #&& cd /tmp \
36 | # && wget http://archive.apache.org/dist/thrift/$VERSION/thrift-$VERSION.tar.gz \
37 | # && mkdir -p /tmp/thrift \
38 | # && tar -C /tmp/thrift -xzf thrift-$VERSION.tar.gz \
39 | # && cd /tmp/thrift/thrift-$VERSION \
40 | # && ./configure --enable-static \
41 | # && make \
42 | # && make install
43 |
44 | RUN export VERSION=1.15 \
45 | && cd /tmp \
46 | && wget https://storage.googleapis.com/golang/go$VERSION.linux-amd64.tar.gz \
47 | && tar -C /usr/local -xzf go$VERSION.linux-amd64.tar.gz \
48 | && rm -fr /go \
49 | && mkdir -m 0666 /go
50 |
51 | ENV GOROOT=/usr/local/go
52 | ENV PATH=$PATH:$GOROOT/bin
53 | ENV GOPATH=/go
54 |
55 | #RUN git clone https://github.com/apache/thrift.git $GOPATH/src/git.apache.org/thrift.git \
56 | # && cd $GOPATH/src/git.apache.org/thrift.git \
57 | # && git checkout 0dd823580c78a79ae9696eb9b3650e400fff140f
58 |
--------------------------------------------------------------------------------
/Dockerfile.common.prod:
--------------------------------------------------------------------------------
1 | FROM debian:buster
2 |
3 | ## Make apt-get non-interactive
4 | ENV DEBIAN_FRONTEND=noninteractive
5 |
6 | RUN apt-get update \
7 | && apt-get install -y \
8 | vim \
9 | curl \
10 | netcat-openbsd \
11 | iproute2 \
12 | supervisor
13 |
14 | COPY debs /debs
15 | RUN dpkg -i /debs/libhiredis0.13_0.13.3-2_amd64.deb \
16 | && dpkg -i /debs/libhiredis-dev_0.13.3-2_amd64.deb \
17 | && dpkg -i /debs/libnl-3-200_3.2.27-1_amd64.deb \
18 | && dpkg -i /debs/libnl-genl-3-200_3.2.27-1_amd64.deb \
19 | && dpkg -i /debs/libnl-route-3-200_3.2.27-1_amd64.deb \
20 | && dpkg -i /debs/libyang_1.0.73_amd64.deb \
21 | && dpkg -i /debs/libswsscommon_1.0.0_amd64.deb \
22 | && dpkg -i /debs/libswsscommon-dev_1.0.0_amd64.deb \
23 | && dpkg -i /debs/sonic-rest-api_1.0.1_amd64.deb \
24 | && dpkg -i /debs/arpresponder-bm_1.0.0_amd64.deb \
25 | && dpkg -i /debs/arpresponder-msee_1.0.0_amd64.deb
26 | RUN rm -fr /debs
27 |
28 | COPY supervisor/supervisor.conf /etc/supervisor/conf.d/
29 |
30 | ENTRYPOINT ["/usr/bin/supervisord"]
31 |
--------------------------------------------------------------------------------
/Dockerfile.msee.prod:
--------------------------------------------------------------------------------
1 | FROM rest-api-common
2 |
3 | RUN apt-get -y install libqt5network5
4 |
5 | COPY supervisor/arp_responder_msee.conf /etc/supervisor/conf.d/
6 | COPY supervisor/rest_api_prod.conf /etc/supervisor/conf.d/
7 |
8 | COPY debs/libthrift-0.9.3_0.9.3-2_amd64.deb /tmp
9 | RUN dpkg -i /tmp/libthrift-0.9.3_0.9.3-2_amd64.deb ; rm -f /tmp/*.deb
10 |
11 | RUN apt-get autoremove -y \
12 | && apt-get clean \
13 | && rm -fr /var/lib/apt/lists/* /tmp/* /var/tmp/*
14 |
--------------------------------------------------------------------------------
/Dockerfile.test:
--------------------------------------------------------------------------------
1 | FROM debian:buster
2 |
3 | ## Make apt-get non-interactive
4 | ENV DEBIAN_FRONTEND=noninteractive
5 |
6 | RUN echo "deb http://archive.debian.org/debian buster-backports main" >> /etc/apt/sources.list
7 |
8 | RUN apt-get update \
9 | && apt-get install -y \
10 | vim \
11 | redis-server \
12 | supervisor \
13 | curl \
14 | bridge-utils \
15 | net-tools \
16 | procps \
17 | libboost-serialization1.71-dev \
18 | libzmq5-dev
19 |
20 | COPY debs /debs
21 | RUN dpkg -i /debs/*.deb || apt-get --fix-broken -y install
22 | RUN rm -fr /debs
23 |
24 | # Adjust redis configurations
25 | RUN sed -ri 's/^(save .*$)/# \1/g; \
26 | s/^daemonize yes$/daemonize no/; \
27 | s/^logfile .*$/logfile ""/; \
28 | s/^# syslog-enabled no$/syslog-enabled no/; \
29 | s/^client-output-buffer-limit pubsub [0-9]+mb [0-9]+mb [0-9]+/client-output-buffer-limit pubsub 0 0 0/; \
30 | s/^bind 127.0.0.1/bind 0.0.0.0/ \
31 | ' /etc/redis/redis.conf
32 |
33 | COPY supervisor/redis.conf /etc/supervisor/conf.d/
34 | COPY supervisor/supervisor.conf /etc/supervisor/conf.d/
35 | COPY supervisor/rest_api_test.conf /etc/supervisor/conf.d/
36 |
37 | COPY start.sh /usr/bin
38 |
39 | COPY CreateMockPort.sh /usr/bin
40 |
41 | COPY debug/* /usr/bin/
42 | RUN mkdir /usr/sbin/cert
43 | RUN mkdir /usr/sbin/cert/client
44 | RUN mkdir /usr/sbin/cert/server
45 | COPY cert/client/* /usr/sbin/cert/client/
46 | COPY cert/server/* /usr/sbin/cert/server/
47 | COPY files/server_kill.py /usr/bin/server_kill.py
48 |
49 | RUN ln -s -f /usr/sbin/cert/client/restapiclient.crt.1 /usr/sbin/cert/client/restapiclient.crt
50 | RUN ln -s -f /usr/sbin/cert/server/restapiserver.crt.1 /usr/sbin/cert/server/restapiserver.crt
51 | RUN ln -s -f /usr/sbin/cert/server/restapiserver.key.1 /usr/sbin/cert/server/restapiserver.key
52 |
53 | RUN apt-get autoremove -y \
54 | && apt-get clean \
55 | && rm -fr /var/lib/apt/lists/* /tmp/* /var/tmp/*
56 |
57 | ENTRYPOINT ["/usr/bin/supervisord"]
58 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2016 Microsoft, Inc.
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: all install build libcswsscommon clean
2 |
3 | GO := /usr/local/go/bin/go
4 | export GOROOT=/usr/local/go
5 | export GOPATH=$(HOME)/go
6 | export GOBIN=$(GOPATH)/bin
7 | export GO111MODULE=on
8 | export GOFLAGS=-modcacherw
9 | RACE_OPTION := -race
10 | ifeq ($(CONFIGURED_ARCH),armhf)
11 | RACE_OPTION :=
12 | endif
13 |
14 | all: install build
15 |
16 | install: build
17 | /usr/bin/install -D $(GOPATH)/bin/go-server-server debian/sonic-rest-api/usr/sbin/go-server-server
18 | /usr/bin/install -D $(GOPATH)/bin/go-server-server.test debian/sonic-rest-api/usr/sbin/go-server-server.test
19 |
20 | build: $(GOPATH)/bin/go-server-server $(GOPATH)/bin/go-server-server.test
21 |
22 | $(GOPATH)/bin/go-server-server: libcswsscommon $(GOPATH)/src/go-server-server/main.go
23 | cd $(GOPATH)/src/go-server-server && $(GO) get -v && $(GO) build $(RACE_OPTION) -v -o $(GOPATH)/bin/go-server-server
24 |
25 | $(GOPATH)/bin/go-server-server.test: libcswsscommon $(GOPATH)/src/go-server-server/main.go
26 | cd $(GOPATH)/src/go-server-server && $(GO) get -v && $(GO) test $(RACE_OPTION) -c -covermode=atomic -coverpkg "go-server-server/go" -v -o $(GOPATH)/bin/go-server-server.test
27 |
28 | $(GOPATH)/src/go-server-server/main.go:
29 | mkdir -p $(GOPATH)/src
30 | cp -r go-server-server $(GOPATH)/src/go-server-server
31 | cp -r swsscommon $(GOPATH)/src/swsscommon
32 |
33 | libcswsscommon:
34 | make -C libcswsscommon
35 | sudo make -C libcswsscommon install
36 |
37 | clean:
38 | rm -rf $(GOPATH)
39 | make -C libcswsscommon clean
40 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SONiC-restapi
2 |
3 | ## Description
4 | This is a configuration agent which exposes HTTPS endpoints to perform dynamic network configuration on switches running SONiC. It restful API server is `go-server-server`
5 |
6 | ## Getting Started
7 | ### Build Rest-API
8 | 1. Execute ./build.sh
9 | 2. The above should generate 2 Docker images, 1 which is used for local development on your VM and 1 which is used for deployment on a TOR
10 | 3. Run `docker images` to check if rest-api dockers were generated
11 |
`REPOSITORY TAG IMAGE ID CREATED SIZE`
12 | `rest-api-image latest d2815fcb7356 2 days ago 222MB`
13 | `rest-api-image-test_local latest e62219a0bae2 2 days ago 222MB`
14 | 4. `rest-api-image-test_local` is for local testing on a dev VM and `rest-api-image` is for TOR testing/deployment
15 | 5. The production image is also stored into a compressed archive `rest-api-image.gz`
16 | ### Running Rest-API container
17 | #### Run Rest-API container locally on a VM and execute unit tests
18 | 1. `docker run -d --rm -p8090:8090 -p6379:6379 --name rest-api --cap-add NET_ADMIN --privileged -t rest-api-image-test_local:latest`
19 | 2. `cd test`
20 | 3. `pytest -v`
21 |
22 | #### Login to Rest-API container and check logs
23 | 1. `docker exec -it rest-api bash`
24 | 2. `vim /tmp/rest-api.err.log`
25 |
26 | #### Run Rest-API container on a switch
27 | 1. scp/copy over the generated archive(`rest-api-image.gz`) to your switch
28 | 2. `docker load < rest-api-image.gz`
29 | 3. `docker run -d -p=8090:8090/tcp -v /var/run/redis/redis.sock:/var/run/redis/redis.sock --name rest-api --cap-add NET_ADMIN --privileged -t rest-api-image:latest`
30 |
--------------------------------------------------------------------------------
/arp_responder/.gitignore:
--------------------------------------------------------------------------------
1 | arpresponder_msee
2 | arpresponder_msee_client
3 | arpresponder_bm
4 | *.o
5 | arp_responder.cpp
6 | arp_responder.h
7 | arp_responder_constants.cpp
8 | arp_responder_constants.h
9 | arp_responder_server.skeleton.cpp
10 | arp_responder_types.cpp
11 | arp_responder_types.h
12 | tests/ptf.*
13 | tests/ptf_tests/*.pyc
14 | tests/build/dep/*.deb
15 | tests/ptf_tests/arp_responder/*
16 |
--------------------------------------------------------------------------------
/arp_responder/Makefile:
--------------------------------------------------------------------------------
1 | .INTERMEDIATE: arp_responder.thrift.i
2 |
3 | all: arpresponder_msee arpresponder_msee_client arpresponder_bm
4 |
5 | install: arpresponder_msee arpresponder_msee_client arpresponder_bm
6 | /usr/bin/install -D arpresponder_msee_client $(DESTDIR)/usr/bin/arpresponder_msee_client
7 | /usr/bin/install -D arpresponder_msee $(DESTDIR)/usr/sbin/arpresponder_msee
8 | /usr/bin/install -D arpresponder_bm $(DESTDIR)/usr/sbin/arpresponder_bm
9 |
10 | cppcheck:
11 | cppcheck --enable=all --inconclusive .
12 |
13 | CFLAGS += --std=gnu++11 -ggdb -O0 -Wall -Wextra -Wlogical-op -Wold-style-cast -Wuseless-cast -Wshadow -Wformat=2
14 |
15 | arpresponder_msee_client: arpresponder_msee_client.o fmt.o arp_responder.o arp_responder_constants.o arp_responder_types.o
16 | g++ -o arpresponder_msee_client arpresponder_msee_client.o fmt.o arp_responder.o arp_responder_constants.o arp_responder_types.o -lthrift
17 |
18 | arpresponder_msee: arpresponder_msee_main.o fmt.o intf.o poller.o arpresponder_msee.o cmd.o arp.o arp_responder.o arp_responder_constants.o arp_responder_types.o
19 | g++ -o arpresponder_msee arpresponder_msee_main.o fmt.o intf.o poller.o arpresponder_msee.o cmd.o arp.o arp_responder.o arp_responder_constants.o arp_responder_types.o -lpthread -lthrift
20 |
21 | arpresponder_msee_client.o: arpresponder_msee_client.cc arp_responder.h
22 | g++ $(CFLAGS) -c $< -o $@
23 |
24 | fmt.o: fmt.cc fmt.h log.h
25 | g++ $(CFLAGS) -c $< -o $@
26 |
27 | intf.o: intf.cc intf.h log.h eintr.h
28 | g++ $(CFLAGS) -c $< -o $@
29 |
30 | poller.o: poller.cc poller.h log.h eintr.h
31 | g++ $(CFLAGS) -c $< -o $@
32 |
33 | cmd.o: cmd.cc cmd.h log.h eintr.h
34 | g++ $(CFLAGS) -c $< -o $@
35 |
36 | arp.o: arp.cc arp.h log.h
37 | g++ $(CFLAGS) -c $< -o $@
38 |
39 | arpresponder_msee_main.o: arpresponder_msee_main.cc arp_responder.h
40 | g++ $(CFLAGS) -c $< -o $@
41 |
42 | arpresponder_msee.o: arpresponder_msee.cc arpresponder_msee.h cmd.h log.h arp.h
43 | g++ $(CFLAGS) -c $< -o $@
44 |
45 | arp_responder.o: arp_responder.cpp arp_responder.h
46 | g++ $(CFLAGS) -c $< -o $@
47 |
48 | arp_responder_constants.o: arp_responder_constants.cpp arp_responder_constants.h
49 | g++ $(CFLAGS) -c $< -o $@
50 |
51 | arp_responder_types.o: arp_responder_types.cpp arp_responder_types.h
52 | g++ $(CFLAGS) -c $< -o $@
53 |
54 | arp_responder.h arp_responder.cpp arp_responder_constants.h arp_responder_constants.cpp arp_responder_types.h arp_responder_types.cpp: arp_responder.thrift.i
55 |
56 | arp_responder.thrift.i: arp_responder.thrift
57 | thrift --out . --gen cpp $<
58 |
59 | arpresponder_bm: arpresponder_bm_main.o arpresponder_bm.o
60 | g++ -o arpresponder_bm arpresponder_bm_main.o arpresponder_bm.o poller.o intf.o arp.o fmt.o
61 |
62 | arpresponder_bm.o: arpresponder_bm.cc arpresponder_bm.h fmt.h log.h intf.h poller.h
63 | g++ $(CFLAGS) -c $< -o $@
64 |
65 | arpresponder_bm_main.o: arpresponder_bm_main.cc arpresponder_bm.h
66 | g++ $(CFLAGS) -c $< -o $@
67 |
68 | clean:
69 | rm -f *.o arpresponder_msee arpresponder_msee_client arpresponder_bm
70 | rm -f arp_responder.h arp_responder.cpp arp_responder_constants.h arp_responder_constants.cpp arp_responder_types.h arp_responder_types.cpp arp_responder_server.skeleton.cpp
71 |
--------------------------------------------------------------------------------
/arp_responder/ToDo:
--------------------------------------------------------------------------------
1 | ToDo:
2 |
3 | 1. Use standard log library for logging
4 | 2. Better to use standard thrift serialize/unserialize logic instead of cmd class
5 | 3. The user of the arp responder should periodically rerequest mac addresses for IP's to update them
6 | 4. arpresponder has a leaked abstraction from cmd
7 | 5. Somewhere struct type, somewhere just type
8 | 6. use this-> or omit it
9 | 7. member_variable_ ?
10 | 8. exceptions
11 |
--------------------------------------------------------------------------------
/arp_responder/arp.cc:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include "intf.h"
7 | #include "fmt.h"
8 | #include "arp.h"
9 |
10 |
11 | void Arp::populate_src_info(const Interface& iface)
12 | {
13 | (void) ::memcpy(ð_hdr->ether_shost, iface.get_mac(), ETH_ALEN);
14 | (void) ::memcpy(&arp_hdr->arp_sha, iface.get_mac(), ETH_ALEN);
15 | }
16 |
17 | void Arp::make_request(uint32_t tip, uint32_t sip)
18 | {
19 | (void) ::memset(ð_hdr->ether_dhost, 0xff, ETH_ALEN);
20 |
21 | arp_hdr->ea_hdr.ar_op = htons(ARPOP_REQUEST);
22 |
23 | uint32_t source_ip = htonl(sip);
24 | uint32_t requested_ip = htonl(tip);
25 |
26 | (void) ::memset(&arp_hdr->arp_tha, 0xff, ETH_ALEN);
27 | (void) ::memcpy(&arp_hdr->arp_spa, &source_ip, IP_ALEN);
28 | (void) ::memcpy(&arp_hdr->arp_tpa, &requested_ip, IP_ALEN);
29 | }
30 |
31 | void Arp::make_reply_from_request(const Interface& iface)
32 | {
33 | (void) ::memcpy(ð_hdr->ether_dhost, ð_hdr->ether_shost, ETH_ALEN);
34 |
35 | arp_hdr->ea_hdr.ar_op = htons(ARPOP_REPLY);
36 |
37 | uint8_t tmp[IP_ALEN];
38 | (void) ::memcpy(tmp, &arp_hdr->arp_tpa, IP_ALEN);
39 | (void) ::memcpy(&arp_hdr->arp_tpa, &arp_hdr->arp_spa, IP_ALEN);
40 | (void) ::memcpy(&arp_hdr->arp_spa, tmp, IP_ALEN);
41 |
42 | (void) ::memcpy(&arp_hdr->arp_tha, &arp_hdr->arp_sha, ETH_ALEN);
43 |
44 | this->populate_src_info(iface);
45 | }
46 |
47 | std::string Arp::dump() const
48 | {
49 | std::ostringstream stream;
50 | for(size_t i = 0; i < packet_len; i++)
51 | stream << s_fmt("%02x ", packet_ptr[i]);
52 |
53 | return stream.str();
54 | }
55 |
56 | MSEEArp::MSEEArp()
57 | {
58 | this->init();
59 | }
60 |
61 | MSEEArp::MSEEArp(const Interface& iface, uint16_t stag, uint16_t ctag)
62 | {
63 | this->init();
64 |
65 | eth_hdr->ether_type = htons(ETHERTYPE_8021AD);
66 | stag_hdr->vlan_tci = htons(stag);
67 | stag_hdr->ether_type = htons(ETHERTYPE_VLAN);
68 | ctag_hdr->vlan_tci = htons(ctag);
69 | ctag_hdr->ether_type = htons(ETHERTYPE_ARP);
70 |
71 | arp_hdr->ea_hdr.ar_hrd = htons(ARPHRD_ETHER);
72 | arp_hdr->ea_hdr.ar_pro = htons(ETH_P_IP);
73 | arp_hdr->ea_hdr.ar_hln = ETH_ALEN;
74 | arp_hdr->ea_hdr.ar_pln = IP_ALEN;
75 |
76 | this->populate_src_info(iface);
77 | }
78 |
79 | void MSEEArp::init()
80 | {
81 | packet_ptr = packet;
82 | packet_len = msee_arp_packet_len;
83 | bzero(packet, msee_arp_packet_len);
84 | eth_hdr = reinterpret_cast(packet);
85 | stag_hdr = reinterpret_cast(ð_hdr[1]);
86 | ctag_hdr = &stag_hdr[1];
87 | arp_hdr = reinterpret_cast(&ctag_hdr[1]);
88 | }
89 |
90 | RawArp::RawArp()
91 | {
92 | this->init();
93 | }
94 |
95 | RawArp::RawArp(const Interface& iface)
96 | {
97 | this->init();
98 |
99 | eth_hdr->ether_type = htons(ETHERTYPE_ARP);
100 |
101 | arp_hdr->ea_hdr.ar_hrd = htons(ARPHRD_ETHER);
102 | arp_hdr->ea_hdr.ar_pro = htons(ETH_P_IP);
103 | arp_hdr->ea_hdr.ar_hln = ETH_ALEN;
104 | arp_hdr->ea_hdr.ar_pln = IP_ALEN;
105 |
106 | this->populate_src_info(iface);
107 | }
108 |
109 | void RawArp::init()
110 | {
111 | packet_ptr = packet;
112 | packet_len = bm_arp_packet_len;
113 | bzero(packet, bm_arp_packet_len);
114 | eth_hdr = reinterpret_cast(packet);
115 | arp_hdr = reinterpret_cast(ð_hdr[1]);
116 | }
117 |
--------------------------------------------------------------------------------
/arp_responder/arp.h:
--------------------------------------------------------------------------------
1 | #ifndef __MAC_H
2 | #define __MAC_H
3 |
4 | #define ETHERTYPE_8021AD ETH_P_8021AD
5 | #define IP_ALEN 4
6 |
7 | struct vlan
8 | {
9 | u_short vlan_tci;
10 | u_short ether_type;
11 | };
12 | // FIXME: can't find struct vlan
13 |
14 | // QinQ ARP packet
15 | const int msee_arp_packet_len = sizeof(struct ether_header) + sizeof(struct vlan) + sizeof(struct vlan) + sizeof(struct ether_arp);
16 |
17 | // RAW ARP packet
18 | const int bm_arp_packet_len = sizeof(struct ether_header) + sizeof(struct ether_arp);
19 |
20 | class Arp
21 | {
22 | public:
23 | void make_request(uint32_t tip, uint32_t sip);
24 | void make_reply_from_request(const Interface& iface);
25 |
26 | virtual bool is_valid() const=0;
27 |
28 | uint8_t get_type() const
29 | {
30 | return ntohs(arp_hdr->ea_hdr.ar_op);
31 | }
32 |
33 | uint8_t* get_packet() const
34 | {
35 | return packet_ptr;
36 | }
37 |
38 | size_t size() const
39 | {
40 | return packet_len;
41 | }
42 |
43 | uint8_t* get_src_mac() const
44 | {
45 | return arp_hdr->arp_sha;
46 | }
47 |
48 | uint8_t* get_dst_mac() const
49 | {
50 | return arp_hdr->arp_tha;
51 | }
52 |
53 | uint32_t get_src_ip() const
54 | {
55 | return ntohl(*reinterpret_cast(&arp_hdr->arp_spa));
56 | }
57 |
58 | uint32_t get_dst_ip() const
59 | {
60 | return ntohl(*reinterpret_cast(&arp_hdr->arp_tpa));
61 | }
62 |
63 | std::string dump() const;
64 |
65 | protected:
66 | virtual void init()=0;
67 | void populate_src_info(const Interface& iface);
68 |
69 | struct ether_header* eth_hdr;
70 | struct ether_arp* arp_hdr;
71 | uint8_t* packet_ptr;
72 | size_t packet_len;
73 | };
74 |
75 |
76 | class MSEEArp : public Arp
77 | {
78 | public:
79 | MSEEArp();
80 | MSEEArp(const Interface& iface, uint16_t stag, uint16_t ctag);
81 |
82 | bool is_valid() const
83 | {
84 | if (eth_hdr->ether_type != htons(ETH_P_8021AD)) return false;
85 | if (stag_hdr->ether_type != htons(ETHERTYPE_VLAN)) return false;
86 | if (ctag_hdr->ether_type != htons(ETHERTYPE_ARP)) return false;
87 |
88 | return true;
89 | }
90 |
91 | uint16_t get_stag() const
92 | {
93 | return ntohs(stag_hdr->vlan_tci);
94 | }
95 |
96 | uint16_t get_ctag() const
97 | {
98 | return ntohs(ctag_hdr->vlan_tci);
99 | }
100 |
101 | private:
102 | void init();
103 |
104 | uint8_t packet[msee_arp_packet_len];
105 | struct vlan* stag_hdr;
106 | struct vlan* ctag_hdr;
107 | };
108 |
109 | class RawArp : public Arp
110 | {
111 | public:
112 | RawArp();
113 | explicit RawArp(const Interface& iface);
114 |
115 | bool is_valid() const
116 | {
117 | return eth_hdr->ether_type == htons(ETHERTYPE_ARP);
118 | }
119 |
120 | private:
121 | void init();
122 |
123 | uint8_t packet[bm_arp_packet_len];
124 | };
125 |
126 | #endif // __MAC_H
127 |
--------------------------------------------------------------------------------
/arp_responder/arp_responder.thrift:
--------------------------------------------------------------------------------
1 | typedef i16 vlan_tag_t
2 | typedef i32 ip4_t
3 | typedef binary mac_t
4 |
5 | struct req_tuple_t
6 | {
7 | 1: string iface_name;
8 | 2: vlan_tag_t stag;
9 | 3: vlan_tag_t ctag;
10 | }
11 |
12 | struct req_tuples_t
13 | {
14 | 1: list tuples;
15 | 2: i32 index;
16 | 3: ip4_t ip;
17 | }
18 |
19 | struct rep_tuple_t
20 | {
21 | 1: req_tuple_t request;
22 | 2: i32 index;
23 | 3: mac_t mac; // presented when is_found is true
24 | 4: bool is_found;
25 | }
26 |
27 | service arp_responder
28 | {
29 | bool add_interface(1: string iface_name);
30 | bool del_interface(1: string iface_name);
31 | bool add_ip(1: string iface_name, 2: vlan_tag_t stag, 3: vlan_tag_t ctag, 4: ip4_t ip);
32 | bool del_ip(1: string iface_name, 2: vlan_tag_t stag, 3: vlan_tag_t ctag);
33 | list request_mac(1: list requests);
34 | }
35 |
--------------------------------------------------------------------------------
/arp_responder/arpresponder_bm.cc:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include "fmt.h"
8 | #include "log.h"
9 | #include "intf.h"
10 | #include "poller.h"
11 | #include "arp.h"
12 | #include "arpresponder_bm.h"
13 |
14 |
15 | ARPResponder::ARPResponder()
16 | {
17 | poller = new Poller();
18 |
19 | LOG_INFO("Starting arpresponder");
20 | }
21 |
22 | ARPResponder::~ARPResponder()
23 | {
24 | LOG_INFO("Stopping arpresonder");
25 |
26 | for (auto intf_pair: interfaces)
27 | {
28 | poller->del_fd(intf_pair.first);
29 | intf_pair.second->close();
30 | delete intf_pair.second;
31 | }
32 |
33 | delete poller;
34 | }
35 |
36 | void ARPResponder::run()
37 | {
38 | LOG_INFO("Starting main loop of arpresponder");
39 |
40 | std::vector fds(MAX_NUM_OF_INTERFACES);
41 | while(true)
42 | {
43 | fds.clear();
44 | poller->poll(fds);
45 | for(auto fd: fds)
46 | process(fd);
47 | }
48 | }
49 |
50 | void ARPResponder::process(const int fd)
51 | {
52 | RawArp arp;
53 | Interface* iface = interfaces[fd];
54 | auto ret = iface->recv(arp.get_packet(), arp.size());
55 | if (ret == -1) return;
56 |
57 | if (!arp.is_valid()) return;
58 |
59 | if (arp.get_type() == ARPOP_REQUEST)
60 | {
61 | LOG_DEBUG("Got arp request on %s ip=%s", iface->get_name().c_str(), s_ip(arp.get_dst_ip()).c_str());
62 |
63 | arp.make_reply_from_request(*iface);
64 | (void)iface->send(arp.get_packet(), arp.size());
65 | }
66 | }
67 |
68 | void ARPResponder::add_interface(const std::string& iface_name)
69 | {
70 | struct sock_filter arp_code_bpf[] = {
71 | { 0x28, 0, 0, 0x0000000c }, // (000) ldh [12]
72 | { 0x15, 0, 1, 0x00000806 }, // (001) jeq #0x806 jt 2 jf 3
73 | { 0x6, 0, 0, 0x00040000 }, // (002) ret #262144
74 | { 0x6, 0, 0, 0x00000000 }, // (003) ret #0
75 | };
76 | // generated by tcpdump -ieth0 -d arp
77 |
78 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
79 |
80 | struct sock_fprog arp_bpf = {
81 | .len = ARRAY_SIZE(arp_code_bpf),
82 | .filter = arp_code_bpf
83 | };
84 |
85 | LOG_DEBUG("Adding interface %s", iface_name.c_str());
86 | Interface* iface = new Interface(iface_name);
87 | iface->open(&arp_bpf);
88 | interfaces[iface->get_fd()] = iface;
89 | poller->add_fd(iface->get_fd());
90 | }
91 |
--------------------------------------------------------------------------------
/arp_responder/arpresponder_bm.h:
--------------------------------------------------------------------------------
1 | #ifndef __ARPRESPONDER_BM_H
2 | #define __ARPRESPONDER_BM_H
3 |
4 | const size_t MAX_NUM_OF_INTERFACES = 64;
5 |
6 | class ARPResponder
7 | {
8 | public:
9 | ARPResponder();
10 | ~ARPResponder();
11 | void add_interface(const std::string& iface_name);
12 | void run();
13 | private:
14 | ARPResponder(const ARPResponder&) {};
15 | void process(const int fd);
16 |
17 | Poller* poller;
18 |
19 | std::unordered_map interfaces;
20 | };
21 |
22 | #endif // __ARPRESPONDER_BM_H
23 |
--------------------------------------------------------------------------------
/arp_responder/arpresponder_bm_main.cc:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include