├── .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 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "fmt.h" 13 | #include "log.h" 14 | #include "intf.h" 15 | #include "poller.h" 16 | #include "cmd.h" 17 | #include "arpresponder_bm.h" 18 | 19 | 20 | bool read_config(const std::string& filename, std::vector& result) 21 | { 22 | std::string line; 23 | 24 | std::ifstream infile(filename); 25 | if (!infile) 26 | { 27 | std::cerr << "Can't open file:" << filename << std::endl; 28 | return false; 29 | } 30 | 31 | while (std::getline(infile, line)) 32 | { 33 | if (!line.empty()) 34 | result.push_back(line); 35 | } 36 | 37 | return true; 38 | } 39 | 40 | int main() 41 | { 42 | std::vector interfaces; 43 | 44 | if (!read_config("/tmp/arpresponder.conf", interfaces)) 45 | return -1; 46 | 47 | ARPResponder responder; 48 | for (auto interface: interfaces) 49 | { 50 | LOG_INFO("Adding interface: %s", interface.c_str()); 51 | responder.add_interface(interface); 52 | } 53 | 54 | responder.run(); 55 | 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /arp_responder/arpresponder_msee.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "fmt.h" 11 | #include "log.h" 12 | #include "intf.h" 13 | #include "poller.h" 14 | #include "cmd.h" 15 | #include "arp.h" 16 | #include "arpresponder_msee.h" 17 | 18 | 19 | ARPResponder::ARPResponder(int control_fd) 20 | { 21 | poller = new Poller(); 22 | cmd = new Cmd(control_fd); 23 | 24 | ctrl_fd = control_fd; 25 | poller->add_fd(ctrl_fd); 26 | 27 | mac_request = request_tuples_t(); 28 | 29 | LOG_INFO("Starting arpresponder"); 30 | } 31 | 32 | ARPResponder::~ARPResponder() 33 | { 34 | LOG_INFO("Stopping arpresonder"); 35 | 36 | for (auto intf_pair: interfaces) 37 | poller->del_fd(intf_pair.first); 38 | 39 | poller->del_fd(ctrl_fd); 40 | 41 | delete cmd; 42 | delete poller; 43 | } 44 | 45 | void ARPResponder::run() 46 | { 47 | LOG_INFO("Starting main loop of arpresponder"); 48 | 49 | std::vector fds(MAX_NUM_OF_INTERFACES + 1); 50 | while(true) 51 | { 52 | fds.clear(); 53 | poller->poll(fds); 54 | for(auto fd: fds) 55 | process(fd); 56 | timeout_requests(); 57 | } 58 | } 59 | 60 | void ARPResponder::process(const int fd) 61 | { 62 | if (fd == ctrl_fd) 63 | process_ctrl(); 64 | else 65 | process_intf(fd); 66 | } 67 | 68 | void ARPResponder::process_ctrl() 69 | { 70 | LOG_DEBUG("ARPResponder::received ctrl request"); 71 | 72 | struct cmd_request request; 73 | 74 | cmd->recv_request(request); 75 | 76 | switch(request.code) 77 | { 78 | case ADD_INTERFACE: 79 | add_interface(request); 80 | break; 81 | case DEL_INTERFACE: 82 | del_interface(request); 83 | break; 84 | case ADD_IP: 85 | add_ip(request); 86 | break; 87 | case DEL_IP: 88 | del_ip(request); 89 | break; 90 | case REQUEST_MAC_ADD: 91 | request_mac_add(request); 92 | break; 93 | case REQUEST_MAC_COMPLETE: 94 | request_mac_complete(request); 95 | break; 96 | case REQUEST_MAC_DO: 97 | request_mac_do(request); 98 | break; 99 | case REQUEST_MAC_POLL: 100 | request_mac_poll(request); 101 | break; 102 | default: 103 | throw std::out_of_range(s_fmt("ARPResponder::process_ctrl: request code is invalid")); 104 | } 105 | } 106 | 107 | void ARPResponder::add_interface(const struct cmd_request& request) 108 | { 109 | struct sock_filter arp_code_bpf[] = { 110 | { 0x20, 0, 0, 0xfffff030 }, // (000) ldh vlan_avail 111 | { 0x15, 0, 5, 0x00000001 }, // (001) jeq #0x1 jt 2 jf 7 112 | { 0x28, 0, 0, 0x0000000c }, // (002) ldh [12] 113 | { 0x15, 0, 3, 0x00008100 }, // (003) jeq #0x8100 jt 4 jf 7 114 | { 0x28, 0, 0, 0x00000010 }, // (004) ldh [16] 115 | { 0x15, 0, 1, 0x00000806 }, // (005) jeq #0x806 jt 6 jf 7 116 | { 0x6, 0, 0, 0x00040000 }, // (006) ret #262144 117 | { 0x6, 0, 0, 0x00000000 }, // (007) ret #0 118 | }; 119 | // generated manually based on tcpdump -ieth0 -d ether[12:2]=0x8100 and ether[16:2]=0x0806 120 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 121 | 122 | struct sock_fprog arp_bpf = { 123 | .len = ARRAY_SIZE(arp_code_bpf), 124 | .filter = arp_code_bpf 125 | }; 126 | 127 | if (fd_interfaces.find(request.interface) == end(fd_interfaces)) 128 | { 129 | LOG_INFO("Adding interface %s", request.interface); 130 | Interface* iface; 131 | try 132 | { 133 | iface = new Interface(request.interface); 134 | } 135 | catch(const std::runtime_error& e) 136 | { 137 | LOG_ERR("Invalid interface %s: %s", request.interface, e.what()); 138 | cmd->resp_invalid_interface(); 139 | return; 140 | } 141 | 142 | iface->open(&arp_bpf); 143 | int intf_fd = iface->get_fd(); 144 | interfaces[intf_fd] = iface; 145 | fd_interfaces[request.interface] = intf_fd; 146 | poller->add_fd(intf_fd); 147 | } 148 | else 149 | { 150 | LOG_DEBUG("Interface %s already exists", request.interface); 151 | } 152 | cmd->resp_interface_added(); 153 | } 154 | 155 | void ARPResponder::del_interface(const struct cmd_request& request) 156 | { 157 | if (fd_interfaces.find(request.interface) != end(fd_interfaces)) 158 | { 159 | LOG_INFO("Removing interface %s", request.interface); 160 | 161 | int intf_fd = fd_interfaces[request.interface]; 162 | fd_interfaces.erase(request.interface); 163 | poller->del_fd(intf_fd); 164 | Interface* iface = interfaces[intf_fd]; 165 | iface->close(); 166 | delete iface; 167 | } 168 | else 169 | { 170 | LOG_DEBUG("Interface %s doesn't exist", request.interface); 171 | } 172 | 173 | cmd->resp_interface_deleted(); 174 | } 175 | 176 | void ARPResponder::request_mac_add(const struct cmd_request& request) 177 | { 178 | if (fd_interfaces.find(request.interface) == end(fd_interfaces)) 179 | { 180 | cmd->resp_request_mac_added(); 181 | return; 182 | } 183 | 184 | request_tuple_t t; 185 | 186 | t.iface_name = std::string(request.interface); 187 | t.stag = request.stag; 188 | t.ctag = request.ctag; 189 | 190 | mac_request.tuples.push_back(t); 191 | 192 | cmd->resp_request_mac_added(); 193 | } 194 | 195 | void ARPResponder::request_mac_complete(const struct cmd_request& request) 196 | { 197 | mac_request.index = request.index; 198 | mac_request.ip = request.ip; 199 | 200 | mac_requests.push_back(mac_request); 201 | mac_request.tuples.clear(); 202 | 203 | cmd->resp_request_mac_completed(); 204 | } 205 | 206 | 207 | void ARPResponder::request_mac_do(const struct cmd_request& request) 208 | { 209 | (void) request; 210 | for (auto r: mac_requests) 211 | for (auto r1: r.tuples) 212 | { 213 | int intf_fd = fd_interfaces[r1.iface_name]; 214 | Interface* iface = interfaces[intf_fd]; 215 | 216 | auto proxy_key = tag_key_t(iface->get_name(), r1.stag, r1.ctag); 217 | 218 | if (proxy_arp.find(proxy_key) == end(proxy_arp)) 219 | { 220 | LOG_ERR("Can't send ARP request on interface %s stag=%u ctag=%u. Interface ip doesn't exist", 221 | iface->get_name().c_str(), r1.stag, r1.ctag); 222 | continue; 223 | } 224 | 225 | MSEEArp arp(*iface, r1.stag, r1.ctag); 226 | arp.make_request(r.ip, proxy_arp[proxy_key]); 227 | auto ret = iface->send(arp.get_packet(), arp.size()); 228 | if (ret == -1) continue; 229 | 230 | LOG_INFO("Requesting mac address for ip %s. Interface=%s stag=%u ctag=%u", 231 | s_ip(r.ip).c_str(), iface->get_name().c_str(), r1.stag, r1.ctag); 232 | 233 | waitlist_key_t key = std::make_tuple(r1.stag, r1.ctag, r.ip); 234 | if (waitlist.find(key) == end(waitlist)) 235 | waitlist[key] = std::make_tuple(r.index, ::time(0), std::set()); 236 | std::get<2>(waitlist[key]).insert(intf_fd); 237 | ready[r.index] = std::make_tuple(MAC_NOT_READY, "", 0, 0, ""); 238 | } 239 | 240 | mac_requests.clear(); 241 | 242 | cmd->resp_request_mac_done(); 243 | } 244 | 245 | void ARPResponder::request_mac_poll(const struct cmd_request& request) 246 | { 247 | if (ready.find(request.index) == end(ready)) 248 | { 249 | cmd->resp_request_mac_not_found(); // FIXME: invalid mac 250 | return; 251 | } 252 | 253 | if (std::get<0>(ready[request.index]) == MAC_NOT_READY) 254 | { 255 | cmd->resp_request_mac_not_ready(); 256 | return; 257 | } 258 | 259 | if (std::get<0>(ready[request.index]) == MAC_NOT_FOUND) 260 | cmd->resp_request_mac_not_found(); 261 | else 262 | { 263 | auto interface = std::get<1>(ready[request.index]); 264 | uint16_t stag = std::get<2>(ready[request.index]); 265 | uint16_t ctag = std::get<3>(ready[request.index]); 266 | const uint8_t* mac = reinterpret_cast(std::get<4>(ready[request.index]).data()); 267 | cmd->resp_request_mac_found(interface, stag, ctag, mac); 268 | } 269 | } 270 | 271 | void ARPResponder::timeout_requests() 272 | { 273 | std::vector keys_for_removing; 274 | 275 | for (auto w: waitlist) 276 | { 277 | if (std::get<1>(w.second) + REPLY_TIMEOUT > time(0)) continue; 278 | 279 | auto stag = std::get<0>(w.first); 280 | auto ctag = std::get<1>(w.first); 281 | auto requested_ip = std::get<2>(w.first); 282 | LOG_INFO("The request for ip %s (s=%d c=%d) was timed out", 283 | s_ip(requested_ip).c_str(), stag, ctag); 284 | ready[std::get<0>(w.second)] = std::make_tuple(MAC_NOT_FOUND, "", 0, 0, ""); 285 | keys_for_removing.push_back(w.first); 286 | } 287 | 288 | for (auto k: keys_for_removing) 289 | waitlist.erase(k); 290 | } 291 | 292 | void ARPResponder::process_intf(const int fd) 293 | { 294 | MSEEArp arp; 295 | Interface* iface = interfaces[fd]; 296 | auto ret = iface->recv(arp.get_packet(), arp.size()); 297 | if (ret == -1) return; 298 | 299 | if (!arp.is_valid()) return; 300 | 301 | LOG_DEBUG("ARP packet dump %s", arp.dump().c_str()); 302 | 303 | if (arp.get_type() == ARPOP_REQUEST) 304 | { 305 | auto key = tag_key_t(iface->get_name(), arp.get_stag(), arp.get_ctag()); 306 | 307 | if (proxy_arp.find(key) == end(proxy_arp)) 308 | { 309 | LOG_DEBUG("Got arp request on %s stag=%u ctag=%u with non-existent ip=%s", 310 | iface->get_name().c_str(), arp.get_stag(), arp.get_ctag(), s_ip(arp.get_dst_ip()).c_str()); 311 | return; 312 | } 313 | 314 | LOG_DEBUG("Responding on arp request on %s stag=%u ctag=%u ip=%s", 315 | iface->get_name().c_str(), arp.get_stag(), arp.get_ctag(), s_ip(proxy_arp[key]).c_str()); 316 | 317 | arp.make_reply_from_request(*iface); 318 | (void)iface->send(arp.get_packet(), arp.size()); 319 | } 320 | 321 | if (arp.get_type() == ARPOP_REPLY) 322 | { 323 | waitlist_key_t key = std::make_tuple(arp.get_stag(), arp.get_ctag(), arp.get_src_ip()); 324 | if (waitlist.find(key) == end(waitlist)) return; 325 | 326 | if (std::get<2>(waitlist[key]).count(iface->get_fd()) == 0) return; 327 | 328 | LOG_INFO("Got arp response on %s stag=%u ctag=%u for ip %s: %s", 329 | iface->get_name().c_str(), arp.get_stag(), arp.get_ctag(), s_ip(arp.get_src_ip()).c_str(), s_mac(arp.get_src_mac()).c_str()); 330 | int index = std::get<0>(waitlist[key]); 331 | auto mac = std::string(reinterpret_cast(arp.get_src_mac()), ETH_ALEN); 332 | ready[index] = std::make_tuple(MAC_FOUND, iface->get_name(), arp.get_stag(), arp.get_ctag(), mac); 333 | waitlist.erase(key); 334 | } 335 | } 336 | 337 | void ARPResponder::add_ip(const struct cmd_request& request) 338 | { 339 | if (request.stag == 0 || request.stag > 4095 340 | || request.ctag == 0 || request.ctag > 4095 341 | || fd_interfaces.find(request.interface) == end(fd_interfaces)) 342 | { 343 | LOG_ERR("Invalid parameters for adding ip: i=%s stag=%u ctag=%u", 344 | request.interface, request.stag, request.ctag); 345 | cmd->resp_invalid_ip(); 346 | return; 347 | } 348 | LOG_INFO("Adding ip %s stag:%u ctag:%u on %s", 349 | s_ip(request.ip).c_str(), request.stag, request.ctag, request.interface); 350 | 351 | auto key = tag_key_t(request.interface, request.stag, request.ctag); 352 | 353 | if (proxy_arp.find(key) != end(proxy_arp) && proxy_arp[key] != request.ip) 354 | { 355 | LOG_INFO("Overwrite ip address from %s to %s. i=%s s=%d c=%d", 356 | s_ip(proxy_arp[key]).c_str(), s_ip(request.ip).c_str(), 357 | request.interface, request.stag, request.ctag); 358 | } 359 | 360 | proxy_arp[key] = request.ip; 361 | cmd->resp_ip_added(); 362 | } 363 | 364 | void ARPResponder::del_ip(const struct cmd_request& request) 365 | { 366 | LOG_INFO("Removing ip stag:%u ctag:%u on %s", 367 | request.stag, request.ctag, request.interface); 368 | 369 | auto key = tag_key_t(request.interface, request.stag, request.ctag); 370 | 371 | if (proxy_arp.find(key) == end(proxy_arp)) 372 | { 373 | cmd->resp_invalid_ip(); 374 | return; 375 | } 376 | proxy_arp.erase(proxy_arp.find(key)); 377 | cmd->resp_ip_deleted(); 378 | } 379 | 380 | -------------------------------------------------------------------------------- /arp_responder/arpresponder_msee.h: -------------------------------------------------------------------------------- 1 | #ifndef __ARPRESPONDER_H 2 | #define __ARPRESPONDER_H 3 | 4 | const size_t MAX_NUM_OF_INTERFACES = 64; 5 | 6 | typedef std::tuple tag_key_t; 7 | typedef std::tuple waitlist_key_t; // stag, ctag, ip 8 | typedef std::tuple> waitlist_value_t; // index, requested_time, set 9 | 10 | enum mac_status_t 11 | { 12 | MAC_NOT_READY, 13 | MAC_NOT_FOUND, 14 | MAC_FOUND 15 | }; 16 | 17 | typedef std::tuple ready_value_t; 18 | // is_ready, interface, stag, ctag, mac_address 19 | 20 | class ARPResponder 21 | { 22 | public: 23 | explicit ARPResponder(int control_fd); 24 | ~ARPResponder(); 25 | void run(); 26 | private: 27 | ARPResponder() {}; 28 | ARPResponder(const ARPResponder&) {}; 29 | void process(const int fd); 30 | void process_ctrl(); 31 | void process_intf(const int fd); 32 | void add_interface(const struct cmd_request& request); 33 | void del_interface(const struct cmd_request& request); 34 | void request_mac_add(const struct cmd_request& request); 35 | void request_mac_complete(const struct cmd_request& request); 36 | void request_mac_do(const struct cmd_request& request); 37 | void request_mac_poll(const struct cmd_request& request); 38 | void timeout_requests(); 39 | void add_ip(const struct cmd_request& request); 40 | void del_ip(const struct cmd_request& request); 41 | 42 | Poller* poller; 43 | Cmd* cmd; 44 | std::unordered_map interfaces; 45 | std::unordered_map fd_interfaces; 46 | 47 | std::map proxy_arp; 48 | 49 | std::vector mac_requests; 50 | request_tuples_t mac_request; 51 | 52 | std::map waitlist; 53 | std::map ready; 54 | 55 | int ctrl_fd; 56 | 57 | const int REPLY_TIMEOUT = 2; 58 | }; 59 | 60 | #endif // __ARPRESPONDER_H 61 | -------------------------------------------------------------------------------- /arp_responder/arpresponder_msee_client.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "arp_responder.h" 8 | 9 | #include "fmt.h" 10 | 11 | 12 | using namespace apache::thrift; 13 | using namespace apache::thrift::protocol; 14 | using namespace apache::thrift::transport; 15 | 16 | void prepare_tuples(std::vector& tuples) 17 | { 18 | req_tuples_t tuple; 19 | req_tuple_t r; 20 | 21 | r.iface_name = "eth0"; 22 | r.stag = 10; 23 | r.ctag = 20; 24 | tuple.tuples.push_back(r); 25 | 26 | r.iface_name = "eth0"; 27 | r.stag = 50; 28 | r.ctag = 100; 29 | tuple.tuples.push_back(r); 30 | 31 | tuple.index = 0; 32 | tuple.ip = 0x11223344; 33 | 34 | tuples.push_back(tuple); 35 | 36 | r.iface_name = "eth0"; 37 | r.stag = 20; 38 | r.ctag = 30; 39 | tuple.tuples.push_back(r); 40 | 41 | r.iface_name = "eth0"; 42 | r.stag = 60; 43 | r.ctag = 110; 44 | tuple.tuples.push_back(r); 45 | 46 | tuple.index = 1; 47 | tuple.ip = 0x55660000; 48 | 49 | tuples.push_back(tuple); 50 | } 51 | 52 | int main() 53 | { 54 | boost::shared_ptr socket(new TSocket("localhost", 9091)); 55 | boost::shared_ptr transport(new TBufferedTransport(socket)); 56 | boost::shared_ptr protocol(new TBinaryProtocol(transport)); 57 | arp_responderClient* client = new arp_responderClient(protocol); 58 | 59 | try 60 | { 61 | bool res; 62 | transport->open(); 63 | std::cout << "Starting client" << std::endl; 64 | 65 | res = client->add_interface("eth0"); 66 | std::cout << "Added interface eth0. Result:" << res << std::endl; 67 | 68 | res = client->add_ip("eth0", 10, 20, 0x0a140001); 69 | std::cout << "Added ip 10.20.0.1 to eth0(10, 20). Result:" << res << std::endl; 70 | 71 | res = client->add_ip("eth0", 50, 100, 0x32640001); 72 | std::cout << "Added ip 50.100.0.1 to eth0(50, 100). Result:" << res << std::endl; 73 | 74 | std::vector tuples; 75 | std::vector responses; 76 | prepare_tuples(tuples); 77 | client->request_mac(responses, tuples); 78 | 79 | for (auto r: responses) 80 | { 81 | std::cout << "Response index=" << r.index; 82 | if (r.is_found) 83 | { 84 | std::cout << " i=" << r.request.iface_name << " s=" << r.request.stag << " c=" << r.request.ctag; 85 | std::cout << " mac=" << s_mac(reinterpret_cast(r.mac.data())) << std::endl; 86 | } 87 | else 88 | { 89 | std::cout << " mac wasn't found" << std::endl; 90 | } 91 | } 92 | 93 | res = client->del_ip("eth0", 50, 100); 94 | std::cout << "Deleted ip 50.100.0.1 to eth0(50, 100). Result:" << res << std::endl; 95 | 96 | res = client->del_ip("eth0", 10, 20); 97 | std::cout << "Deleted ip 10.20.0.1 to eth0(10, 20). Result:" << res << std::endl; 98 | 99 | res = client->del_interface("eth0"); 100 | std::cout << "Deleted interface eth0. Result:" << res << std::endl; 101 | 102 | std::cout << "Stopping client" << std::endl; 103 | } 104 | catch (TException& tx) 105 | { 106 | std::cout << "ERROR: " << tx.what() << std::endl; 107 | } 108 | 109 | return 0; 110 | } 111 | -------------------------------------------------------------------------------- /arp_responder/arpresponder_msee_main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "fmt.h" 11 | #include "log.h" 12 | #include "intf.h" 13 | #include "poller.h" 14 | #include "cmd.h" 15 | #include "arpresponder_msee.h" 16 | 17 | #include "arp_responder.h" 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | using namespace ::apache::thrift; 24 | using namespace ::apache::thrift::protocol; 25 | using namespace ::apache::thrift::transport; 26 | using namespace ::apache::thrift::server; 27 | 28 | using boost::shared_ptr; 29 | 30 | void arp_responder(int fd) 31 | { 32 | ARPResponder responder(fd); 33 | responder.run(); 34 | } 35 | 36 | class arp_responderHandler : virtual public arp_responderIf 37 | { 38 | public: 39 | arp_responderHandler() 40 | { 41 | int res = ::socketpair(AF_LOCAL, SOCK_DGRAM, 0, fd_); 42 | if (res == -1) 43 | throw std::runtime_error(s_fmt("Can't create socket pair: error=(%d):%s", errno, strerror(errno))); 44 | 45 | arp_ = std::thread(arp_responder, fd_[0]); 46 | cmd_ = new Cmd(fd_[1]); 47 | } 48 | 49 | ~arp_responderHandler() 50 | { 51 | (void) ::close(fd_[0]); 52 | (void) ::close(fd_[1]); 53 | } 54 | 55 | bool add_interface(const std::string& iface_name) 56 | { 57 | return cmd_->add_interface(iface_name); 58 | } 59 | 60 | bool del_interface(const std::string& iface_name) 61 | { 62 | return cmd_->del_interface(iface_name); 63 | } 64 | 65 | bool add_ip(const std::string& iface_name, const vlan_tag_t stag, const vlan_tag_t ctag, const ip4_t ip) 66 | { 67 | return cmd_->add_ip(iface_name, stag, ctag, ip); 68 | } 69 | 70 | bool del_ip(const std::string& iface_name, const vlan_tag_t stag, const vlan_tag_t ctag) 71 | { 72 | return cmd_->del_ip(iface_name, stag, ctag); 73 | } 74 | 75 | void request_mac(std::vector& _return, const std::vector& requests) 76 | { 77 | std::vector req; 78 | std::vector resp; 79 | 80 | for (auto r: requests) 81 | { 82 | request_tuples_t rt; 83 | rt.index = r.index; 84 | rt.ip = r.ip; 85 | for (auto r1: r.tuples) 86 | { 87 | struct request_tuple_t rt1; 88 | rt1.iface_name = r1.iface_name; 89 | rt1.stag = r1.stag; 90 | rt1.ctag = r1.ctag; 91 | rt.tuples.push_back(rt1); 92 | } 93 | req.push_back(rt); 94 | } 95 | 96 | cmd_->request_mac(resp, req); 97 | 98 | for (auto r: resp) 99 | { 100 | rep_tuple_t rt; 101 | rt.index = r.index; 102 | rt.is_found = r.is_found; 103 | rt.mac = std::string(reinterpret_cast(r.mac), ETH_ALEN); 104 | rt.request.iface_name = r.request.iface_name; 105 | rt.request.stag = r.request.stag; 106 | rt.request.ctag = r.request.ctag; 107 | _return.push_back(rt); 108 | } 109 | } 110 | 111 | private: 112 | int fd_[2]; 113 | std::thread arp_; 114 | Cmd* cmd_; 115 | }; 116 | 117 | int main() 118 | { 119 | int port = 9091; 120 | shared_ptr handler(new arp_responderHandler()); 121 | shared_ptr processor(new arp_responderProcessor(handler)); 122 | shared_ptr serverTransport(new TServerSocket(port)); 123 | shared_ptr transportFactory(new TBufferedTransportFactory()); 124 | shared_ptr protocolFactory(new TBinaryProtocolFactory()); 125 | 126 | TSimpleServer server(processor, serverTransport, transportFactory, protocolFactory); 127 | server.serve(); 128 | return 0; 129 | } 130 | 131 | -------------------------------------------------------------------------------- /arp_responder/cmd.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "eintr.h" 11 | #include "fmt.h" 12 | #include "cmd.h" 13 | 14 | 15 | // send interface 16 | bool Cmd::add_interface(const std::string& iface_name) 17 | { 18 | return this->mod_interface(iface_name, ADD_INTERFACE, INTERFACE_ADDED); 19 | } 20 | 21 | bool Cmd::del_interface(const std::string& iface_name) 22 | { 23 | return this->mod_interface(iface_name, DEL_INTERFACE, INTERFACE_DELETED); 24 | } 25 | 26 | bool Cmd::mod_interface(const std::string& iface_name, enum cmd_request_code code, enum cmd_response_code expected) 27 | { 28 | struct cmd_request request; 29 | bzero(&request, request_len); 30 | 31 | request.code = code; 32 | (void) ::strncpy(request.interface, iface_name.c_str(), IFNAMSIZ); 33 | 34 | this->send_request(request); 35 | 36 | struct cmd_response response; 37 | this->recv_response(response); 38 | 39 | return response.code == expected; 40 | } 41 | 42 | bool Cmd::add_ip(const std::string& iface_name, uint16_t stag, uint16_t ctag, uint32_t ip) 43 | { 44 | return this->mod_ip(iface_name, stag, ctag, ip, ADD_IP, IP_ADDED); 45 | } 46 | 47 | bool Cmd::del_ip(const std::string& iface_name, uint16_t stag, uint16_t ctag) 48 | { 49 | return this->mod_ip(iface_name, stag, ctag, 0, DEL_IP, IP_DELETED); 50 | } 51 | 52 | bool Cmd::mod_ip(const std::string& iface_name, uint16_t stag, uint16_t ctag, uint32_t ip, 53 | enum cmd_request_code code, enum cmd_response_code expected) 54 | { 55 | struct cmd_request request; 56 | bzero(&request, request_len); 57 | 58 | request.code = code; 59 | (void) ::strncpy(request.interface, iface_name.c_str(), IFNAMSIZ); 60 | request.stag = stag; 61 | request.ctag = ctag; 62 | request.ip = ip; 63 | 64 | this->send_request(request); 65 | 66 | struct cmd_response response; 67 | this->recv_response(response); 68 | 69 | return response.code == expected; 70 | } 71 | 72 | bool Cmd::request_mac(std::vector& responses, const std::vector& requests) 73 | { 74 | std::unordered_set poll; 75 | for (auto request: requests) 76 | { 77 | for (auto tuple: request.tuples) 78 | request_mac_intf(tuple, request.index); 79 | 80 | request_mac_complete(request.ip); 81 | 82 | poll.insert(request.index); 83 | } 84 | 85 | request_mac_do(); 86 | 87 | timespec pause_req = { 88 | .tv_sec = 0, 89 | .tv_nsec = 500000000, 90 | }; 91 | timespec pause_rem; 92 | 93 | while (!poll.empty()) 94 | { 95 | std::unordered_set to_remove; 96 | 97 | for (auto index: poll) 98 | { 99 | response_tuple_t r; 100 | bool result = request_mac_poll(index, r.request.iface_name, r.request.stag, r.request.ctag, r.mac, r.is_found); 101 | if (result) 102 | { 103 | r.index = index; 104 | responses.push_back(r); 105 | to_remove.insert(index); 106 | } 107 | } 108 | 109 | for (auto index: to_remove) 110 | poll.erase(index); 111 | 112 | if (!poll.empty()) 113 | (void) ::nanosleep(&pause_req, &pause_rem); 114 | } 115 | 116 | return true; 117 | } 118 | 119 | void Cmd::request_mac_intf(const request_tuple_t& tuple, int index) 120 | { 121 | struct cmd_request request; 122 | ::bzero(&request, request_len); 123 | 124 | request.code = REQUEST_MAC_ADD; 125 | (void) ::strncpy(request.interface, tuple.iface_name.c_str(), IFNAMSIZ); 126 | request.stag = tuple.stag; 127 | request.ctag = tuple.ctag; 128 | request.index = index; 129 | 130 | this->send_request(request); 131 | 132 | struct cmd_response response; 133 | this->recv_response(response); 134 | 135 | if (response.code != REQUEST_MAC_ADDED) 136 | throw std::out_of_range(s_fmt("Cmd::request_mac_intf: response code is invalid")); 137 | } 138 | 139 | void Cmd::request_mac_complete(uint32_t ip) 140 | { 141 | struct cmd_request request; 142 | 143 | ::bzero(&request, request_len); 144 | request.code = REQUEST_MAC_COMPLETE; 145 | request.ip = ip; 146 | this->send_request(request); 147 | 148 | struct cmd_response response; 149 | this->recv_response(response); 150 | assert(response.code == REQUEST_MAC_COMPLETED); 151 | } 152 | 153 | void Cmd::request_mac_do() 154 | { 155 | struct cmd_request request; 156 | 157 | ::bzero(&request, request_len); 158 | request.code = REQUEST_MAC_DO; 159 | this->send_request(request); 160 | 161 | struct cmd_response response; 162 | this->recv_response(response); 163 | assert(response.code == REQUEST_MAC_DONE); 164 | } 165 | 166 | bool Cmd::request_mac_poll(int index, std::string& iface, uint16_t& stag, uint16_t& ctag, uint8_t mac[ETH_ALEN], bool& is_found) 167 | { 168 | struct cmd_request request; 169 | 170 | ::bzero(&request, request_len); 171 | request.code = REQUEST_MAC_POLL; 172 | request.index = index; 173 | this->send_request(request); 174 | 175 | struct cmd_response response; 176 | this->recv_response(response); 177 | if (response.code == REQUEST_MAC_NOT_READY) return false; 178 | 179 | if (response.code == REQUEST_MAC_FOUND) 180 | { 181 | iface = std::string(response.interface); 182 | stag = response.stag; 183 | ctag = response.ctag; 184 | (void) ::memcpy(mac, response.mac, ETH_ALEN); 185 | is_found = true; 186 | } 187 | else 188 | { 189 | assert(response.code == REQUEST_MAC_NOT_FOUND); 190 | ::bzero(mac, ETH_ALEN); 191 | is_found = false; 192 | } 193 | 194 | return true; 195 | } 196 | 197 | // receive interface 198 | void Cmd::resp_interface_added() 199 | { 200 | this->send_simple_response(INTERFACE_ADDED); 201 | } 202 | 203 | void Cmd::resp_interface_deleted() 204 | { 205 | this->send_simple_response(INTERFACE_DELETED); 206 | } 207 | 208 | void Cmd::resp_invalid_interface() 209 | { 210 | this->send_simple_response(INVALID_INTERFACE); 211 | } 212 | 213 | void Cmd::resp_ip_added() 214 | { 215 | this->send_simple_response(IP_ADDED); 216 | } 217 | 218 | void Cmd::resp_ip_deleted() 219 | { 220 | this->send_simple_response(IP_DELETED); 221 | } 222 | 223 | void Cmd::resp_invalid_ip() 224 | { 225 | this->send_simple_response(INVALID_IP); 226 | } 227 | 228 | void Cmd::resp_request_mac_added() 229 | { 230 | this->send_simple_response(REQUEST_MAC_ADDED); 231 | } 232 | 233 | void Cmd::resp_request_mac_completed() 234 | { 235 | this->send_simple_response(REQUEST_MAC_COMPLETED); 236 | } 237 | 238 | void Cmd::resp_request_mac_done() 239 | { 240 | this->send_simple_response(REQUEST_MAC_DONE); 241 | } 242 | 243 | void Cmd::resp_request_mac_found(std::string iface, uint16_t stag, uint16_t ctag, const uint8_t mac[ETH_ALEN]) 244 | { 245 | struct cmd_response resp; 246 | resp.code = REQUEST_MAC_FOUND; 247 | 248 | (void) ::strncpy(resp.interface, iface.c_str(), IFNAMSIZ); 249 | resp.stag = stag; 250 | resp.ctag = ctag; 251 | (void) ::memcpy(resp.mac, mac, ETH_ALEN); 252 | 253 | this->send_response(resp); 254 | } 255 | 256 | void Cmd::resp_request_mac_not_found() 257 | { 258 | this->send_simple_response(REQUEST_MAC_NOT_FOUND); 259 | } 260 | 261 | void Cmd::resp_request_mac_not_ready() 262 | { 263 | this->send_simple_response(REQUEST_MAC_NOT_READY); 264 | } 265 | 266 | void Cmd::send_simple_response(enum cmd_response_code code) 267 | { 268 | struct cmd_response resp; 269 | resp.code = code; 270 | 271 | this->send_response(resp); 272 | } 273 | 274 | void Cmd::send_request(const struct cmd_request& req) 275 | { 276 | this->send(reinterpret_cast(&req), request_len); 277 | } 278 | 279 | void Cmd::recv_request(struct cmd_request& req) 280 | { 281 | this->recv(reinterpret_cast(&req), request_len); 282 | } 283 | 284 | void Cmd::send_response(const struct cmd_response& resp) 285 | { 286 | this->send(reinterpret_cast(&resp), response_len); 287 | } 288 | 289 | void Cmd::recv_response(struct cmd_response& resp) 290 | { 291 | this->recv(reinterpret_cast(&resp), response_len); 292 | } 293 | 294 | void Cmd::send(const char* buf, size_t size) 295 | { 296 | ssize_t ret = HANDLE_EINTR(::send(fd, buf, size, 0)); 297 | if (ret == -1) 298 | throw std::runtime_error(s_fmt("Cmd::send:send: error=(%d):%s", errno, strerror(errno))); 299 | } 300 | 301 | void Cmd::recv(char* buf, size_t size) 302 | { 303 | ssize_t ret = HANDLE_EINTR(::recv(fd, buf, size, 0)); 304 | if (ret == -1) 305 | throw std::runtime_error(s_fmt("Cmd::recv:send: error=(%d):%s", errno, strerror(errno))); 306 | } 307 | 308 | -------------------------------------------------------------------------------- /arp_responder/cmd.h: -------------------------------------------------------------------------------- 1 | #ifndef __CMD_H 2 | #define __CMD_H 3 | 4 | enum cmd_request_code 5 | { 6 | ADD_INTERFACE, 7 | DEL_INTERFACE, 8 | ADD_IP, 9 | DEL_IP, 10 | REQUEST_MAC_ADD, 11 | REQUEST_MAC_COMPLETE, 12 | REQUEST_MAC_DO, 13 | REQUEST_MAC_POLL 14 | }; 15 | 16 | enum cmd_response_code 17 | { 18 | INTERFACE_ADDED, 19 | INTERFACE_DELETED, 20 | INVALID_INTERFACE, 21 | IP_ADDED, 22 | IP_DELETED, 23 | INVALID_IP, 24 | REQUEST_MAC_ADDED, 25 | REQUEST_MAC_COMPLETED, 26 | REQUEST_MAC_DONE, 27 | REQUEST_MAC_FOUND, 28 | REQUEST_MAC_NOT_FOUND, 29 | REQUEST_MAC_NOT_READY 30 | }; 31 | 32 | struct cmd_request 33 | { 34 | cmd_request_code code; 35 | char interface[IFNAMSIZ]; 36 | uint16_t stag; 37 | uint16_t ctag; 38 | uint32_t ip; 39 | int index; 40 | }; 41 | 42 | struct cmd_response 43 | { 44 | cmd_response_code code; 45 | char interface[IFNAMSIZ]; 46 | uint16_t stag; 47 | uint16_t ctag; 48 | uint8_t mac[ETH_ALEN]; 49 | int index; 50 | }; 51 | 52 | struct request_tuple_t 53 | { 54 | std::string iface_name; 55 | uint16_t stag; 56 | uint16_t ctag; 57 | }; 58 | 59 | struct request_tuples_t 60 | { 61 | std::vector tuples; 62 | uint32_t ip; 63 | int index; 64 | }; 65 | 66 | struct response_tuple_t 67 | { 68 | request_tuple_t request; 69 | int index; 70 | uint8_t mac[ETH_ALEN]; 71 | bool is_found; 72 | }; 73 | 74 | const size_t request_len = sizeof(struct cmd_request); 75 | const size_t response_len = sizeof(struct cmd_response); 76 | 77 | class Cmd 78 | { 79 | public: 80 | explicit Cmd(int ctrl_fd) : fd(ctrl_fd) {} 81 | // send interface 82 | bool add_interface(const std::string& iface_name); 83 | bool del_interface(const std::string& iface_name); 84 | bool mod_interface(const std::string& iface_name, enum cmd_request_code code, enum cmd_response_code expected); 85 | bool add_ip(const std::string& iface_name, uint16_t stag, uint16_t ctag, uint32_t ip); 86 | bool del_ip(const std::string& iface_name, uint16_t stag, uint16_t ctag); 87 | bool mod_ip(const std::string& iface_name, uint16_t stag, uint16_t ctag, uint32_t ip, 88 | enum cmd_request_code code, enum cmd_response_code expected); 89 | bool request_mac(std::vector& responses, const std::vector& requests); 90 | 91 | // receive interface 92 | void send_simple_response(enum cmd_response_code code); 93 | void resp_interface_added(); 94 | void resp_interface_deleted(); 95 | void resp_invalid_interface(); 96 | void resp_ip_added(); 97 | void resp_ip_deleted(); 98 | void resp_invalid_ip(); 99 | void resp_request_mac_added(); 100 | void resp_request_mac_completed(); 101 | void resp_request_mac_done(); 102 | void resp_request_mac_found(std::string iface, uint16_t stag, uint16_t ctag, const uint8_t mac[ETH_ALEN]); 103 | 104 | void resp_request_mac_not_found(); 105 | void resp_request_mac_not_ready(); 106 | 107 | void recv_request(struct cmd_request& req); 108 | private: 109 | Cmd() {}; 110 | void request_mac_intf(const request_tuple_t& tuple, int index); 111 | void request_mac_complete(uint32_t ip); 112 | void request_mac_do(); 113 | bool request_mac_poll(int index, std::string& iface, uint16_t& stag, uint16_t& ctag, uint8_t mac[ETH_ALEN], bool& is_found); 114 | void send_request(const struct cmd_request& req); 115 | void send_response(const struct cmd_response& resp); 116 | void recv_response(struct cmd_response& resp); 117 | void send(const char* buf, size_t size); 118 | void recv(char* buf, size_t size); 119 | 120 | int fd; 121 | }; 122 | 123 | #endif // __CMD_H 124 | -------------------------------------------------------------------------------- /arp_responder/debian/arpresponder-bm.install: -------------------------------------------------------------------------------- 1 | usr/sbin/arpresponder_bm 2 | -------------------------------------------------------------------------------- /arp_responder/debian/arpresponder-msee-client.install: -------------------------------------------------------------------------------- 1 | usr/bin/arpresponder_msee_client 2 | -------------------------------------------------------------------------------- /arp_responder/debian/arpresponder-msee.install: -------------------------------------------------------------------------------- 1 | usr/sbin/arpresponder_msee 2 | -------------------------------------------------------------------------------- /arp_responder/debian/changelog: -------------------------------------------------------------------------------- 1 | arpresponder (1.0.0) stable; urgency=medium 2 | 3 | * Initial release. 4 | 5 | -- Pavel Shirshov Wed, 06 Sep 2017 15:42:15 -0800 6 | 7 | 8 | -------------------------------------------------------------------------------- /arp_responder/debian/compat: -------------------------------------------------------------------------------- 1 | 9 2 | 3 | -------------------------------------------------------------------------------- /arp_responder/debian/control: -------------------------------------------------------------------------------- 1 | Source: arpresponder 2 | Maintainer: Pavel Shirshov 3 | Section: net 4 | Priority: optional 5 | Build-Depends: dh-exec (>=0.3), debhelper (>= 9) 6 | Standards-Version: 1.0.0 7 | 8 | Package: arpresponder-msee 9 | Architecture: any 10 | Depends: ${shlibs:Depends} 11 | Description: This package contains arpresponder for MSEE scenario. 12 | 13 | Package: arpresponder-msee-client 14 | Architecture: any 15 | Depends: ${shlibs:Depends} 16 | Description: This package contains arpresponder-client demo for MSEE scenario. 17 | 18 | Package: arpresponder-bm 19 | Architecture: any 20 | Depends: ${shlibs:Depends} 21 | Description: This package contains arpresponder for Baremetal scenario. 22 | -------------------------------------------------------------------------------- /arp_responder/debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | #export DH_VERBOSE = 1 4 | 5 | # see EXAMPLES in dpkg-buildflags(1) and read /usr/share/dpkg/* 6 | DPKG_EXPORT_BUILDFLAGS = 1 7 | include /usr/share/dpkg/default.mk 8 | 9 | # see FEATURE AREAS in dpkg-buildflags(1) 10 | #export DEB_BUILD_MAINT_OPTIONS = hardening=+all 11 | 12 | # see ENVIRONMENT in dpkg-buildflags(1) 13 | # package maintainers to append CFLAGS 14 | #export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic 15 | # package maintainers to append LDFLAGS 16 | #export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed 17 | 18 | 19 | # main packaging script based on dh7 syntax 20 | %: 21 | dh $@ 22 | 23 | override_dh_shlibdeps: 24 | dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info 25 | -------------------------------------------------------------------------------- /arp_responder/eintr.h: -------------------------------------------------------------------------------- 1 | #ifndef __EINTR_H 2 | #define __EINTR_H 3 | 4 | #define HANDLE_EINTR(x) ({ \ 5 | typeof(x) __eintr_result__; \ 6 | do { \ 7 | __eintr_result__ = (x); \ 8 | } while (__eintr_result__ == -1 && errno == EINTR); \ 9 | __eintr_result__; \ 10 | }) 11 | 12 | #endif // __EINTR_H 13 | -------------------------------------------------------------------------------- /arp_responder/fmt.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "fmt.h" 7 | 8 | 9 | std::string s_fmt(const char *fmt, ...) 10 | { 11 | char *result; 12 | va_list ap; 13 | 14 | ::va_start(ap, fmt); 15 | (void) ::vasprintf(&result, fmt, ap); 16 | ::va_end(ap); 17 | 18 | std::string str(result); 19 | ::free(result); 20 | 21 | return str; 22 | } 23 | 24 | std::string s_ip(uint32_t ip) 25 | { 26 | char ip_str[16]; 27 | uint32_t c_ip = htonl(ip); 28 | 29 | const char *result = ::inet_ntop(AF_INET, &c_ip, ip_str, sizeof(ip_str)); 30 | if (result == NULL) 31 | throw std::runtime_error(s_fmt("s_ip::inet_ntop: error=(%d):%s", errno, strerror(errno))); 32 | 33 | return std::string(ip_str); 34 | } 35 | 36 | std::string s_mac(const uint8_t* mac) 37 | { 38 | char mac_str[18]; 39 | 40 | (void) snprintf(mac_str, sizeof(mac_str), "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); 41 | 42 | return std::string(mac_str); 43 | } 44 | -------------------------------------------------------------------------------- /arp_responder/fmt.h: -------------------------------------------------------------------------------- 1 | #ifndef __FMT_H_ 2 | #define __FMT_H_ 3 | 4 | std::string s_fmt(const char *fmt, ...); 5 | std::string s_ip(uint32_t ip); 6 | std::string s_mac(const uint8_t* mac); 7 | 8 | #endif // __FMT_H_ 9 | -------------------------------------------------------------------------------- /arp_responder/intf.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "eintr.h" 12 | #include "fmt.h" 13 | #include "log.h" 14 | #include "intf.h" 15 | 16 | 17 | Interface::Interface(const std::string& iface_name) : name(iface_name),fd(-1) 18 | { 19 | struct ifreq ifr; 20 | 21 | ifindex = ::if_nametoindex(name.c_str()); 22 | if (ifindex == 0) 23 | throw std::runtime_error(s_fmt("Interface(%s)::constructor:if_nametoindex: error=(%d):%s", name.c_str(), errno, strerror(errno))); 24 | 25 | fd = ::socket(PF_INET, SOCK_STREAM, 0); 26 | if (fd == -1) 27 | throw std::runtime_error(s_fmt("Interface(%s)::constructor:socket: error=(%d):%s", name.c_str(), errno, strerror(errno))); 28 | 29 | (void) ::strncpy(ifr.ifr_name, iface_name.c_str(), sizeof(ifr.ifr_name) - 1); 30 | 31 | int res = ::ioctl(fd, SIOCGIFHWADDR, &ifr); 32 | if (res == -1) 33 | throw std::runtime_error(s_fmt("Interface(%s)::constructor:ioctl: error=(%d):%s", name.c_str(), errno, strerror(errno))); 34 | 35 | (void) ::memcpy(mac, ifr.ifr_hwaddr.sa_data, sizeof(mac)); 36 | 37 | (void)HANDLE_EINTR(::close(fd)); 38 | } 39 | 40 | void Interface::open(const struct sock_fprog* bpf) 41 | { 42 | int err_code; 43 | struct sockaddr_ll addr; 44 | 45 | (void) ::memset(&addr, 0, sizeof(addr)); 46 | addr.sll_family = AF_PACKET; 47 | addr.sll_ifindex = ifindex; 48 | 49 | fd = ::socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); 50 | if (fd == -1) 51 | throw std::runtime_error(s_fmt("Interface(%s)::open:socket: error=(%d):%s", name.c_str(), errno, strerror(errno))); 52 | 53 | int val = 1; 54 | err_code = ::setsockopt(fd, SOL_PACKET, PACKET_AUXDATA, &val, sizeof(val)); 55 | if (err_code == -1) 56 | { 57 | close(); 58 | throw std::runtime_error(s_fmt("Interface(%s)::open:setsockopt(auxdata): error=(%d):%s", name.c_str(), errno, strerror(errno))); 59 | } 60 | 61 | if (bpf != 0) 62 | { 63 | err_code = ::setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, bpf, sizeof(*bpf)); 64 | if (err_code == -1) 65 | { 66 | close(); 67 | throw std::runtime_error(s_fmt("Interface(%s)::open:setsockopt(bpf): error=(%d):%s", name.c_str(), errno, strerror(errno))); 68 | } 69 | } 70 | 71 | err_code = ::bind(fd, reinterpret_cast(&addr), sizeof(addr)); 72 | if (err_code == -1) 73 | { 74 | close(); 75 | throw std::runtime_error(s_fmt("Interface(%s)::open:bind: error=(%d):%s", name.c_str(), errno, strerror(errno))); 76 | } 77 | } 78 | 79 | void Interface::close() 80 | { 81 | if (fd != -1) 82 | { 83 | (void)HANDLE_EINTR(::close(fd)); 84 | fd = -1; 85 | } 86 | } 87 | 88 | 89 | ssize_t Interface::recv(uint8_t* buf, size_t len) 90 | { 91 | struct iovec iov; 92 | struct msghdr msg; 93 | union { 94 | struct cmsghdr cmsg; 95 | char buf[CMSG_SPACE(sizeof(struct tpacket_auxdata))]; 96 | } cmsg_buf; 97 | 98 | struct sockaddr_ll from; 99 | 100 | msg.msg_name = &from; 101 | msg.msg_namelen = sizeof(from); 102 | msg.msg_iov = &iov; 103 | msg.msg_iovlen = 1; 104 | msg.msg_control = &cmsg_buf; 105 | msg.msg_controllen = sizeof(cmsg_buf); 106 | msg.msg_flags = 0; 107 | 108 | iov.iov_len = len; 109 | iov.iov_base = buf; 110 | 111 | ssize_t packet_len = HANDLE_EINTR(::recvmsg(fd, &msg, 0)); 112 | if (packet_len == -1) 113 | { 114 | if (errno == ENETDOWN) 115 | { 116 | LOG_INFO("Interface %s is down", name.c_str()); 117 | return -1; 118 | } 119 | else 120 | throw std::runtime_error(s_fmt("Interface(%s)::recv:recvmsg: error=(%d):%s", name.c_str(), errno, strerror(errno))); 121 | } 122 | 123 | for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) 124 | { 125 | struct tpacket_auxdata *aux; 126 | ssize_t r_len; 127 | struct vlan_tag *tag; 128 | 129 | if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct tpacket_auxdata)) 130 | || cmsg->cmsg_level != SOL_PACKET 131 | || cmsg->cmsg_type != PACKET_AUXDATA) 132 | continue; 133 | 134 | aux = (struct tpacket_auxdata *)CMSG_DATA(cmsg); 135 | if ((aux->tp_vlan_tci == 0) && !(aux->tp_status & TP_STATUS_VLAN_VALID)) 136 | continue; 137 | 138 | r_len = packet_len > static_cast(iov.iov_len) ? static_cast(iov.iov_len) : packet_len; 139 | if (r_len < ETH_ALEN*2) break; 140 | 141 | (void) ::memmove(buf + ETH_ALEN*2 + sizeof(struct vlan_tag), buf + ETH_ALEN*2, packet_len - ETH_ALEN*2); 142 | 143 | tag = reinterpret_cast(buf + ETH_ALEN*2); 144 | tag->vlan_tpid = htons(VLAN_TPID(aux, aux)); 145 | tag->vlan_tci = htons(aux->tp_vlan_tci); 146 | } 147 | 148 | return packet_len; 149 | } 150 | 151 | ssize_t Interface::send(const uint8_t* buf, size_t len) 152 | { 153 | struct sockaddr_ll to; 154 | struct sockaddr* to_ptr = reinterpret_cast(&to); 155 | to.sll_family = AF_PACKET; 156 | to.sll_ifindex = ifindex; 157 | to.sll_halen = 6; 158 | (void) ::memcpy(&to.sll_addr, buf, 6); 159 | 160 | ssize_t ret = HANDLE_EINTR(::sendto(fd, buf, len, 0, to_ptr, sizeof(to))); 161 | if (ret == -1) 162 | { 163 | if (errno == ENETDOWN) 164 | { 165 | LOG_INFO("Interface %s is down", name.c_str()); 166 | return -1; 167 | } 168 | else 169 | throw std::runtime_error(s_fmt("Interface(%s)::send:sendto: error=(%d):%s", name.c_str(), errno, strerror(errno))); 170 | } 171 | 172 | return ret; 173 | } 174 | 175 | 176 | -------------------------------------------------------------------------------- /arp_responder/intf.h: -------------------------------------------------------------------------------- 1 | #ifndef __INTF_H 2 | #define __INTF_H 3 | 4 | struct vlan_tag 5 | { 6 | u_short vlan_tpid; 7 | u_short vlan_tci; 8 | }; 9 | 10 | #define VLAN_TPID(hdr, hv) (((hv)->tp_vlan_tpid || ((hdr)->tp_status & TP_STATUS_VLAN_TPID_VALID)) ? (hv)->tp_vlan_tpid : ETH_P_8021Q) 11 | 12 | class Interface 13 | { 14 | public: 15 | explicit Interface(const std::string& iface_name); 16 | void open(const struct sock_fprog* bpf = 0); 17 | void close(); 18 | ssize_t recv(uint8_t* buf, size_t len); 19 | ssize_t send(const uint8_t* buf, size_t len); 20 | 21 | int get_fd() const 22 | { 23 | return fd; 24 | } 25 | 26 | const uint8_t* get_mac() const 27 | { 28 | return mac; 29 | } 30 | 31 | const std::string& get_name() const 32 | { 33 | return name; 34 | } 35 | 36 | private: 37 | Interface() {}; 38 | 39 | std::string name; 40 | int fd; 41 | unsigned int ifindex; 42 | uint8_t mac[6]; 43 | }; 44 | 45 | 46 | #endif // __INTF_H 47 | -------------------------------------------------------------------------------- /arp_responder/log.h: -------------------------------------------------------------------------------- 1 | #ifndef __LOG_H 2 | #define __LOG_H 3 | 4 | enum LOG_LEVEL { 5 | LOG_LEVEL_ERR, 6 | LOG_LEVEL_INFO, 7 | LOG_LEVEL_DEBUG 8 | }; 9 | 10 | #define DEFAULT_LOG_LEVEL LOG_LEVEL_INFO 11 | 12 | #define LOG_ERR(format, ...) do { if (LOG_LEVEL_ERR <= DEFAULT_LOG_LEVEL) { LOG_TS(stderr, "ERR"); (void) ::fprintf(stderr, format, ##__VA_ARGS__); (void) ::fprintf(stderr, "\n"); ::fflush(stderr); }} while(0) 13 | #define LOG_INFO(format, ...) do { if (LOG_LEVEL_INFO <= DEFAULT_LOG_LEVEL) { LOG_TS(stdout, "INFO"); (void) ::fprintf(stdout, format, ##__VA_ARGS__); (void) ::fprintf(stdout, "\n"); ::fflush(stdout); }} while(0) 14 | #define LOG_DEBUG(format, ...) do { if (LOG_LEVEL_DEBUG <= DEFAULT_LOG_LEVEL) { LOG_TS(stdout, "DEBUG"); (void) ::fprintf(stdout, format, ##__VA_ARGS__); (void) ::fprintf(stdout, "\n"); ::fflush(stdout); }} while(0) 15 | #define LOG_TS(where, level) do { \ 16 | time_t raw; \ 17 | struct tm *tm; \ 18 | char ___buf[80]; \ 19 | ::time(&raw); \ 20 | tm = ::localtime(&raw); \ 21 | ::strftime(___buf, sizeof(___buf), "%Y-%m-%d %I:%M:%S", tm); \ 22 | ::fprintf(where, "%s: %s: ", ___buf, level); \ 23 | } while(0) 24 | 25 | #endif // __LOG_H 26 | -------------------------------------------------------------------------------- /arp_responder/poller.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "eintr.h" 6 | #include "fmt.h" 7 | #include "poller.h" 8 | 9 | 10 | Poller::Poller() 11 | { 12 | epoll_fd = ::epoll_create1(0); 13 | if (epoll_fd == -1) 14 | throw std::runtime_error(s_fmt("Poller::constructor:epoll_create1: error=(%d):%s", errno, strerror(errno))); 15 | 16 | cnt = 0; 17 | } 18 | 19 | void Poller::poll(std::vector& ready) 20 | { 21 | struct epoll_event events[MAX_EVENTS]; 22 | 23 | int res = HANDLE_EINTR(::epoll_wait(epoll_fd, events, MAX_EVENTS, EPOLL_TIMEOUT)); 24 | if (res == -1) 25 | throw std::runtime_error(s_fmt("Poller::poll:epoll_wait: error=(%d):%s", errno, strerror(errno))); 26 | 27 | for (int i = 0; i < res; i++) 28 | ready.push_back(events[i].data.fd); 29 | } 30 | 31 | void Poller::add_fd(const int fd) 32 | { 33 | struct epoll_event ev; 34 | 35 | ev.events = EPOLLIN | EPOLLET; 36 | ev.data.fd = fd; 37 | int res = ::epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev); 38 | if (res == -1) 39 | throw std::runtime_error(s_fmt("Poller::add_fd:epoll_ctl: error=(%d):%s", errno, strerror(errno))); 40 | 41 | cnt++; 42 | } 43 | 44 | void Poller::del_fd(const int fd) 45 | { 46 | int res = ::epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL); 47 | if (res == -1) 48 | throw std::runtime_error(s_fmt("Poller::del_fd:epoll_ctl: error=(%d):%s", errno, strerror(errno))); 49 | 50 | cnt--; 51 | } 52 | -------------------------------------------------------------------------------- /arp_responder/poller.h: -------------------------------------------------------------------------------- 1 | #ifndef __POLLER_H 2 | #define __POLLER_H 3 | 4 | const int MAX_EVENTS = 65; 5 | const int EPOLL_TIMEOUT = 1000; // 1 second 6 | 7 | class Poller 8 | { 9 | public: 10 | Poller(); 11 | void poll(std::vector& ready); 12 | void add_fd(const int fd); 13 | void del_fd(const int fd); 14 | int get_fd_counter() const 15 | { 16 | return cnt; 17 | } 18 | private: 19 | int epoll_fd; 20 | int cnt; 21 | }; 22 | 23 | #endif //__POLLER_H 24 | -------------------------------------------------------------------------------- /arp_responder/tests/build/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:latest 2 | 3 | MAINTAINER Pavel Shirshov 4 | 5 | ENV DEBIAN_FRONTEND=noninteractive 6 | 7 | RUN apt-get update \ 8 | && apt-get upgrade -y \ 9 | && apt-get install -y --no-install-recommends \ 10 | openssh-server \ 11 | vim \ 12 | python \ 13 | python-pip \ 14 | python-setuptools \ 15 | supervisor \ 16 | traceroute \ 17 | lsof \ 18 | tcpdump \ 19 | libssl-dev \ 20 | less \ 21 | libboost-all-dev \ 22 | g++ \ 23 | wget \ 24 | make \ 25 | && apt-get -y install -f \ 26 | && rm -rf /root/deps \ 27 | && apt-get -y autoclean \ 28 | && apt-get -y autoremove 29 | 30 | RUN mkdir /var/run/sshd 31 | 32 | RUN echo 'root:123456!' | chpasswd 33 | 34 | ENV LANG C.UTF-8 35 | ENV LANGUAGE C.UTF-8 36 | ENV LC_ALL C.UTF-8 37 | 38 | RUN sed -ri 's/^PermitRootLogin\s+.*/PermitRootLogin yes/' /etc/ssh/sshd_config 39 | RUN sed -ri 's/UsePAM yes/#UsePAM yes/g' /etc/ssh/sshd_config 40 | RUN sed -i '$aUseDNS no' /etc/ssh/sshd_config 41 | 42 | EXPOSE 22 43 | 44 | ADD supervisord.conf /etc/supervisor/conf.d/supervisord.conf 45 | 46 | RUN wget http://archive.apache.org/dist/thrift/0.9.3/thrift-0.9.3.tar.gz \ 47 | && tar xvfz thrift-0.9.3.tar.gz \ 48 | && cd thrift-0.9.3 \ 49 | && ./configure --prefix=/usr \ 50 | && make \ 51 | && make install \ 52 | && cd .. \ 53 | && rm -fr thrift-0.9.3 \ 54 | && rm -f thrift-0.9.3.tar.gz 55 | 56 | ENTRYPOINT ["/usr/bin/supervisord"] 57 | -------------------------------------------------------------------------------- /arp_responder/tests/build/dep/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonic-net/sonic-restapi/4a564baddf7abe8fb904ed9fbb4f713954a84573/arp_responder/tests/build/dep/.gitkeep -------------------------------------------------------------------------------- /arp_responder/tests/build/supervisord.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | nodaemon=true 3 | 4 | [program:sshd] 5 | command=/usr/sbin/sshd -D 6 | process_name=sshd 7 | stdout_logfile=/tmp/sshd.out.log 8 | stderr_logfile=/tmp/sshd.err.log 9 | redirect_stderr=false 10 | autostart=true 11 | autorestart=true 12 | startsecs=1 13 | numprocs=1 14 | 15 | [program:arpresponder_bm] 16 | command=/usr/sbin/arpresponder_bm 17 | process_name=arpresponder_bm 18 | stdout_logfile=/tmp/arpresponder_bm.out.log 19 | stderr_logfile=/tmp/arpresponder_bm.err.log 20 | redirect_stderr=false 21 | autostart=false 22 | autorestart=false 23 | startsecs=1 24 | numprocs=1 25 | 26 | [program:arpresponder_msee] 27 | command=/usr/sbin/arpresponder_msee 28 | process_name=arpresponder_msee 29 | stdout_logfile=/tmp/arpresponder_msee.out.log 30 | stderr_logfile=/tmp/arpresponder_msee.err.log 31 | redirect_stderr=false 32 | autostart=false 33 | autorestart=false 34 | startsecs=1 35 | numprocs=1 36 | 37 | -------------------------------------------------------------------------------- /arp_responder/tests/ptf_tests/bm_tests.py: -------------------------------------------------------------------------------- 1 | import ptf 2 | from ptf.base_tests import BaseTest 3 | import ptf.testutils as testutils 4 | from ptf.testutils import simple_arp_packet 5 | from pprint import pprint 6 | 7 | 8 | class BMEasyTest(BaseTest): 9 | def setUp(self): 10 | self.dataplane = ptf.dataplane_instance 11 | self.dataplane.flush() 12 | self.my_mac = {} 13 | self.remote_mac = {} 14 | for port_id, port in self.dataplane.ports.iteritems(): 15 | self.my_mac[port_id[1]] = port.mac() 16 | self.remote_mac[port_id[1]] = self.get_remote_mac(port_id[1]) 17 | 18 | def tearDown(self): 19 | pass 20 | 21 | def runTest(self): 22 | self.test_port(0) 23 | self.test_port(1) 24 | 25 | def test_port(self, port_number): 26 | src_mac = self.my_mac[port_number] 27 | packet = simple_arp_packet( 28 | eth_src=src_mac, 29 | vlan_vid=0, 30 | vlan_pcp=0, 31 | ip_snd='192.168.0.1', 32 | ip_tgt='192.168.0.2', 33 | hw_snd=src_mac, 34 | hw_tgt='00:00:00:00:00:00', 35 | ) 36 | testutils.send_packet(self, (0, port_number), packet) 37 | 38 | exp_packet = simple_arp_packet( 39 | pktlen=42, 40 | eth_dst=src_mac, 41 | eth_src=self.remote_mac[port_number], 42 | vlan_vid=0, 43 | vlan_pcp=0, 44 | arp_op=2, 45 | ip_snd='192.168.0.2', 46 | ip_tgt='192.168.0.1', 47 | hw_snd=self.remote_mac[port_number], 48 | hw_tgt=src_mac 49 | ) 50 | testutils.verify_packet(self, exp_packet, port_number) 51 | 52 | def get_remote_mac(self, port_number): 53 | mac, _, _ = self.cmd(['docker', 'exec', '-ti', 'arpresponder_test', 'cat', '/sys/class/net/iif%d/address' % port_number]) 54 | return mac.strip() 55 | 56 | def cmd(self, cmds): 57 | process = subprocess.Popen(cmds, 58 | shell=False, 59 | stdout=subprocess.PIPE, 60 | stderr=subprocess.PIPE) 61 | stdout, stderr = process.communicate() 62 | return_code = process.returncode 63 | 64 | return stdout, stderr, return_code 65 | 66 | -------------------------------------------------------------------------------- /arp_responder/tests/ptf_tests/msee_tests.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import ptf 3 | from ptf.base_tests import BaseTest 4 | import ptf.testutils as testutils 5 | from ptf.testutils import simple_arp_packet 6 | import struct 7 | from threading import Thread 8 | from pprint import pprint 9 | 10 | sys.path.append('/usr/lib/python2.7/site-packages') 11 | 12 | from thrift import Thrift 13 | from thrift.transport import TSocket 14 | from thrift.transport import TTransport 15 | from thrift.protocol import TBinaryProtocol 16 | 17 | from arp_responder import arp_responder 18 | from arp_responder.ttypes import req_tuple_t, req_tuples_t, rep_tuple_t 19 | 20 | 21 | class MSEEEasyTest(BaseTest): 22 | def setUp(self): 23 | self.dataplane = ptf.dataplane_instance 24 | self.dataplane.flush() 25 | self.my_mac = {} 26 | self.remote_mac = {} 27 | for port_id, port in self.dataplane.ports.iteritems(): 28 | self.my_mac[port_id[1]] = port.mac() 29 | self.remote_mac[port_id[1]] = self.get_remote_mac(port_id[1]) 30 | 31 | self.result = None 32 | 33 | socket = TSocket.TSocket('localhost', 9091) 34 | self.transport = TTransport.TBufferedTransport(socket) 35 | protocol = TBinaryProtocol.TBinaryProtocol(self.transport) 36 | self.client = arp_responder.Client(protocol) 37 | self.transport.open() 38 | 39 | def tearDown(self): 40 | self.transport.close() 41 | 42 | def runTest(self): 43 | self.test_reply() 44 | self.test_request() 45 | 46 | def test_reply(self): 47 | self.test_reply_qinq(0) 48 | self.test_reply_qinq(1) 49 | 50 | def test_reply_qinq(self, port_number): 51 | intf = 'iif%d' % port_number 52 | stag = 10 53 | ctag = 20 54 | self.client.add_interface(intf) 55 | self.client.add_ip(intf, stag, ctag, 0x01020304) 56 | src_mac = self.my_mac[port_number] 57 | packet = simple_arp_packet( 58 | pktlen=42, 59 | eth_src=src_mac, 60 | ip_snd='1.2.3.1', 61 | ip_tgt='1.2.3.4', 62 | hw_snd=src_mac, 63 | hw_tgt='00:00:00:00:00:00', 64 | ) 65 | tagged_packet = self.insert_tags(packet, stag, ctag) 66 | testutils.send_packet(self, (0, port_number), tagged_packet) 67 | self.client.del_ip(intf, stag, ctag) 68 | self.client.del_interface(intf) 69 | 70 | exp_packet = simple_arp_packet( 71 | pktlen=42, 72 | eth_dst=src_mac, 73 | eth_src=self.remote_mac[port_number], 74 | arp_op=2, 75 | ip_snd='1.2.3.4', 76 | ip_tgt='1.2.3.1', 77 | hw_snd=self.remote_mac[port_number], 78 | hw_tgt=src_mac 79 | ) 80 | tagged_exp_packet = self.insert_tags(exp_packet, stag, ctag) 81 | testutils.verify_packet(self, tagged_exp_packet, port_number) 82 | 83 | def test_request(self): 84 | thr = Thread(target=self.request_mac_thread) 85 | thr.start() 86 | stag = 10 87 | ctag = 20 88 | exp_packet_1 = simple_arp_packet( 89 | pktlen=42, 90 | eth_dst='ff:ff:ff:ff:ff:ff', 91 | eth_src=self.remote_mac[0], 92 | ip_snd='1.2.3.4', 93 | ip_tgt='1.2.3.1', 94 | hw_snd=self.remote_mac[0], 95 | hw_tgt='ff:ff:ff:ff:ff:ff' 96 | ) 97 | t_exp_packet0 = self.insert_tags(exp_packet_1, stag, ctag) 98 | testutils.verify_packet(self, t_exp_packet0, 0) 99 | exp_packet_2 = simple_arp_packet( 100 | pktlen=42, 101 | eth_dst='ff:ff:ff:ff:ff:ff', 102 | eth_src=self.remote_mac[1], 103 | ip_snd='1.2.3.5', 104 | ip_tgt='1.2.3.1', 105 | hw_snd=self.remote_mac[1], 106 | hw_tgt='ff:ff:ff:ff:ff:ff' 107 | ) 108 | t_exp_packet1 = self.insert_tags(exp_packet_2, stag, ctag) 109 | testutils.verify_packet(self, t_exp_packet1, 1) 110 | 111 | packet = simple_arp_packet( 112 | pktlen=42, 113 | eth_dst=self.remote_mac[0], 114 | eth_src=self.my_mac[0], 115 | arp_op=2, 116 | ip_snd='1.2.3.1', 117 | ip_tgt='1.2.3.4', 118 | hw_snd=self.my_mac[0], 119 | hw_tgt=self.remote_mac[0] 120 | ) 121 | tagged_packet = self.insert_tags(packet, stag, ctag) 122 | testutils.send_packet(self, (0, 0), tagged_packet) 123 | thr.join() 124 | result_mac = ":".join("%02x" % v for v in list(struct.unpack("BBBBBB", self.result[0].mac))) 125 | self.assertTrue(self.result[0].index == 0) 126 | self.assertTrue(self.result[0].is_found) 127 | self.assertTrue(result_mac == self.my_mac[0]) 128 | self.assertTrue(self.result[0].request.stag == 10) 129 | self.assertTrue(self.result[0].request.ctag == 20) 130 | self.assertTrue(self.result[0].request.iface_name == 'iif0') 131 | 132 | def request_mac_thread(self): 133 | self.client.add_interface('iif0') 134 | self.client.add_ip('iif0', 10, 20, 0x01020304) 135 | self.client.add_interface('iif1') 136 | self.client.add_ip('iif1', 10, 20, 0x01020305) 137 | 138 | t1 = req_tuples_t([req_tuple_t('iif0', 10, 20), req_tuple_t('iif1', 10, 20)], 0, 0x01020301) 139 | self.result = self.client.request_mac([t1]) 140 | 141 | self.client.del_ip('iif1', 10, 20) 142 | self.client.del_interface('iif1') 143 | self.client.del_ip('iif0', 10, 20) 144 | self.client.del_interface('iif0') 145 | 146 | def get_remote_mac(self, port_number): 147 | mac, _, _ = self.cmd(['docker', 'exec', '-ti', 'arpresponder_test', 'cat', '/sys/class/net/iif%d/address' % port_number]) 148 | return mac.strip() 149 | 150 | def cmd(self, cmds): 151 | process = subprocess.Popen(cmds, 152 | shell=False, 153 | stdout=subprocess.PIPE, 154 | stderr=subprocess.PIPE) 155 | stdout, stderr = process.communicate() 156 | return_code = process.returncode 157 | 158 | return stdout, stderr, return_code 159 | 160 | def insert_tags(self, packet, stag, ctag): 161 | p = str(packet) 162 | vlan_hdr = struct.pack("!HHHH",0x88A8, stag, 0x8100, ctag) 163 | return p[0:12] + vlan_hdr + p[12:] 164 | 165 | 166 | -------------------------------------------------------------------------------- /arp_responder/tests/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [[ $(id -u) -ne 0 ]]; 4 | then 5 | echo Please run as root 6 | exit 7 | fi 8 | 9 | docker images | grep '^autoresponder_test' > /dev/null 10 | if [[ $? -ne 0 ]]; 11 | then 12 | docker build --no-cache \ 13 | --rm=true \ 14 | -t autoresponder_test \ 15 | build 16 | fi 17 | 18 | function add_if() 19 | { 20 | num=$1 21 | pid=$2 22 | extif=eif$num 23 | intif=iif$num 24 | ip link add $extif type veth peer name $intif 25 | ip link set netns $pid dev $intif 26 | ip link set $extif up 27 | nsenter -t $pid -n ip link set $intif up 28 | } 29 | 30 | # Start docker container 31 | docker run -d -it -p9091:9091/tcp --name arpresponder_test --cap-add NET_ADMIN autoresponder_test > /dev/null 32 | 33 | # Insert interfaces inside of the container 34 | docker_id=arpresponder_test 35 | pid=$(docker inspect --format '{{.State.Pid}}' $docker_id) 36 | add_if 0 $pid 37 | add_if 1 $pid 38 | 39 | echo 40 | 41 | # Compile 42 | cd .. 43 | debian/rules clean 44 | dpkg-buildpackage -us -uc 45 | docker cp ../arpresponder-bm_1.0.0_amd64.deb arpresponder_test:/tmp 46 | docker cp ../arpresponder-msee_1.0.0_amd64.deb arpresponder_test:/tmp 47 | docker exec -ti arpresponder_test dpkg -i /tmp/arpresponder-bm_1.0.0_amd64.deb 48 | docker exec -ti arpresponder_test dpkg -i /tmp/arpresponder-msee_1.0.0_amd64.deb 49 | cd tests 50 | 51 | # Test MSEE 52 | thrift --gen py ../arp_responder.thrift 53 | mv gen-py/arp_responder ptf_tests/ 54 | rm -fr gen-py 55 | docker exec -ti arpresponder_test supervisorctl start arpresponder_msee > /dev/null 56 | sudo ptf --disable-ipv6 --test-dir ptf_tests msee_tests -i 0-0@eif0 -i 0-1@eif1 57 | docker exec -ti arpresponder_test supervisorctl stop arpresponder_msee > /dev/null 58 | 59 | echo 60 | 61 | # Test Baremetal 62 | docker exec -ti arpresponder_test bash -c "echo 'iif0' > /tmp/arpresponder.conf" 63 | docker exec -ti arpresponder_test bash -c "echo 'iif1' >> /tmp/arpresponder.conf" 64 | docker exec -ti arpresponder_test supervisorctl start arpresponder_bm > /dev/null 65 | sudo ptf --disable-ipv6 --test-dir ptf_tests bm_tests -i 0-0@eif0 -i 0-1@eif1 66 | docker exec -ti arpresponder_test supervisorctl stop arpresponder_bm > /dev/null 67 | 68 | # Clean up 69 | rm -fr ptf_tests/arp_responder 70 | rm -f ptf.log 71 | rm -f ptf.pcap 72 | docker stop arpresponder_test > /dev/null 73 | docker rm arpresponder_test > /dev/null 74 | -------------------------------------------------------------------------------- /arpthrift/GoUnusedProtection__.go: -------------------------------------------------------------------------------- 1 | // Autogenerated by Thrift Compiler (0.10.0) 2 | // DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 3 | 4 | package arp 5 | 6 | var GoUnusedProtection__ int; 7 | 8 | -------------------------------------------------------------------------------- /arpthrift/arp-consts.go: -------------------------------------------------------------------------------- 1 | // Autogenerated by Thrift Compiler (0.10.0) 2 | // DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 3 | 4 | package arp 5 | 6 | import ( 7 | "bytes" 8 | "fmt" 9 | "git.apache.org/thrift.git/lib/go/thrift" 10 | ) 11 | 12 | // (needed to ensure safety because of naive import list construction.) 13 | var _ = thrift.ZERO 14 | var _ = fmt.Printf 15 | var _ = bytes.Equal 16 | 17 | 18 | func init() { 19 | } 20 | 21 | -------------------------------------------------------------------------------- /arpthrift/arp_responder-remote/arp_responder-remote.go: -------------------------------------------------------------------------------- 1 | // Autogenerated by Thrift Compiler (0.10.0) 2 | // DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 3 | 4 | package main 5 | 6 | import ( 7 | "flag" 8 | "fmt" 9 | "math" 10 | "net" 11 | "net/url" 12 | "os" 13 | "strconv" 14 | "strings" 15 | "git.apache.org/thrift.git/lib/go/thrift" 16 | "arp" 17 | ) 18 | 19 | 20 | func Usage() { 21 | fmt.Fprintln(os.Stderr, "Usage of ", os.Args[0], " [-h host:port] [-u url] [-f[ramed]] function [arg1 [arg2...]]:") 22 | flag.PrintDefaults() 23 | fmt.Fprintln(os.Stderr, "\nFunctions:") 24 | fmt.Fprintln(os.Stderr, " bool add_interface(string iface_name)") 25 | fmt.Fprintln(os.Stderr, " bool del_interface(string iface_name)") 26 | fmt.Fprintln(os.Stderr, " bool add_ip(string iface_name, vlan_tag_t stag, vlan_tag_t ctag, ip4_t ip)") 27 | fmt.Fprintln(os.Stderr, " bool del_ip(string iface_name, vlan_tag_t stag, vlan_tag_t ctag)") 28 | fmt.Fprintln(os.Stderr, " request_mac( requests)") 29 | fmt.Fprintln(os.Stderr) 30 | os.Exit(0) 31 | } 32 | 33 | func main() { 34 | flag.Usage = Usage 35 | var host string 36 | var port int 37 | var protocol string 38 | var urlString string 39 | var framed bool 40 | var useHttp bool 41 | var parsedUrl url.URL 42 | var trans thrift.TTransport 43 | _ = strconv.Atoi 44 | _ = math.Abs 45 | flag.Usage = Usage 46 | flag.StringVar(&host, "h", "localhost", "Specify host and port") 47 | flag.IntVar(&port, "p", 9090, "Specify port") 48 | flag.StringVar(&protocol, "P", "binary", "Specify the protocol (binary, compact, simplejson, json)") 49 | flag.StringVar(&urlString, "u", "", "Specify the url") 50 | flag.BoolVar(&framed, "framed", false, "Use framed transport") 51 | flag.BoolVar(&useHttp, "http", false, "Use http") 52 | flag.Parse() 53 | 54 | if len(urlString) > 0 { 55 | parsedUrl, err := url.Parse(urlString) 56 | if err != nil { 57 | fmt.Fprintln(os.Stderr, "Error parsing URL: ", err) 58 | flag.Usage() 59 | } 60 | host = parsedUrl.Host 61 | useHttp = len(parsedUrl.Scheme) <= 0 || parsedUrl.Scheme == "http" 62 | } else if useHttp { 63 | _, err := url.Parse(fmt.Sprint("http://", host, ":", port)) 64 | if err != nil { 65 | fmt.Fprintln(os.Stderr, "Error parsing URL: ", err) 66 | flag.Usage() 67 | } 68 | } 69 | 70 | cmd := flag.Arg(0) 71 | var err error 72 | if useHttp { 73 | trans, err = thrift.NewTHttpClient(parsedUrl.String()) 74 | } else { 75 | portStr := fmt.Sprint(port) 76 | if strings.Contains(host, ":") { 77 | host, portStr, err = net.SplitHostPort(host) 78 | if err != nil { 79 | fmt.Fprintln(os.Stderr, "error with host:", err) 80 | os.Exit(1) 81 | } 82 | } 83 | trans, err = thrift.NewTSocket(net.JoinHostPort(host, portStr)) 84 | if err != nil { 85 | fmt.Fprintln(os.Stderr, "error resolving address:", err) 86 | os.Exit(1) 87 | } 88 | if framed { 89 | trans = thrift.NewTFramedTransport(trans) 90 | } 91 | } 92 | if err != nil { 93 | fmt.Fprintln(os.Stderr, "Error creating transport", err) 94 | os.Exit(1) 95 | } 96 | defer trans.Close() 97 | var protocolFactory thrift.TProtocolFactory 98 | switch protocol { 99 | case "compact": 100 | protocolFactory = thrift.NewTCompactProtocolFactory() 101 | break 102 | case "simplejson": 103 | protocolFactory = thrift.NewTSimpleJSONProtocolFactory() 104 | break 105 | case "json": 106 | protocolFactory = thrift.NewTJSONProtocolFactory() 107 | break 108 | case "binary", "": 109 | protocolFactory = thrift.NewTBinaryProtocolFactoryDefault() 110 | break 111 | default: 112 | fmt.Fprintln(os.Stderr, "Invalid protocol specified: ", protocol) 113 | Usage() 114 | os.Exit(1) 115 | } 116 | client := arp.NewArpResponderClientFactory(trans, protocolFactory) 117 | if err := trans.Open(); err != nil { 118 | fmt.Fprintln(os.Stderr, "Error opening socket to ", host, ":", port, " ", err) 119 | os.Exit(1) 120 | } 121 | 122 | switch cmd { 123 | case "add_interface": 124 | if flag.NArg() - 1 != 1 { 125 | fmt.Fprintln(os.Stderr, "AddInterface requires 1 args") 126 | flag.Usage() 127 | } 128 | argvalue0 := flag.Arg(1) 129 | value0 := argvalue0 130 | fmt.Print(client.AddInterface(value0)) 131 | fmt.Print("\n") 132 | break 133 | case "del_interface": 134 | if flag.NArg() - 1 != 1 { 135 | fmt.Fprintln(os.Stderr, "DelInterface requires 1 args") 136 | flag.Usage() 137 | } 138 | argvalue0 := flag.Arg(1) 139 | value0 := argvalue0 140 | fmt.Print(client.DelInterface(value0)) 141 | fmt.Print("\n") 142 | break 143 | case "add_ip": 144 | if flag.NArg() - 1 != 4 { 145 | fmt.Fprintln(os.Stderr, "AddIP requires 4 args") 146 | flag.Usage() 147 | } 148 | argvalue0 := flag.Arg(1) 149 | value0 := argvalue0 150 | tmp1, err18 := (strconv.ParseInt(flag.Arg(2), 10, 16)) 151 | if err18 != nil { 152 | Usage() 153 | return 154 | } 155 | argvalue1 := int16(tmp1) 156 | value1 := arp.VlanTagT(argvalue1) 157 | tmp2, err19 := (strconv.ParseInt(flag.Arg(3), 10, 16)) 158 | if err19 != nil { 159 | Usage() 160 | return 161 | } 162 | argvalue2 := int16(tmp2) 163 | value2 := arp.VlanTagT(argvalue2) 164 | tmp3, err20 := (strconv.ParseInt(flag.Arg(4), 10, 32)) 165 | if err20 != nil { 166 | Usage() 167 | return 168 | } 169 | argvalue3 := int32(tmp3) 170 | value3 := arp.Ip4T(argvalue3) 171 | fmt.Print(client.AddIP(value0, value1, value2, value3)) 172 | fmt.Print("\n") 173 | break 174 | case "del_ip": 175 | if flag.NArg() - 1 != 3 { 176 | fmt.Fprintln(os.Stderr, "DelIP requires 3 args") 177 | flag.Usage() 178 | } 179 | argvalue0 := flag.Arg(1) 180 | value0 := argvalue0 181 | tmp1, err22 := (strconv.ParseInt(flag.Arg(2), 10, 16)) 182 | if err22 != nil { 183 | Usage() 184 | return 185 | } 186 | argvalue1 := int16(tmp1) 187 | value1 := arp.VlanTagT(argvalue1) 188 | tmp2, err23 := (strconv.ParseInt(flag.Arg(3), 10, 16)) 189 | if err23 != nil { 190 | Usage() 191 | return 192 | } 193 | argvalue2 := int16(tmp2) 194 | value2 := arp.VlanTagT(argvalue2) 195 | fmt.Print(client.DelIP(value0, value1, value2)) 196 | fmt.Print("\n") 197 | break 198 | case "request_mac": 199 | if flag.NArg() - 1 != 1 { 200 | fmt.Fprintln(os.Stderr, "RequestMac requires 1 args") 201 | flag.Usage() 202 | } 203 | arg24 := flag.Arg(1) 204 | mbTrans25 := thrift.NewTMemoryBufferLen(len(arg24)) 205 | defer mbTrans25.Close() 206 | _, err26 := mbTrans25.WriteString(arg24) 207 | if err26 != nil { 208 | Usage() 209 | return 210 | } 211 | factory27 := thrift.NewTSimpleJSONProtocolFactory() 212 | jsProt28 := factory27.GetProtocol(mbTrans25) 213 | containerStruct0 := arp.NewArpResponderRequestMacArgs() 214 | err29 := containerStruct0.ReadField1(jsProt28) 215 | if err29 != nil { 216 | Usage() 217 | return 218 | } 219 | argvalue0 := containerStruct0.Requests 220 | value0 := argvalue0 221 | fmt.Print(client.RequestMac(value0)) 222 | fmt.Print("\n") 223 | break 224 | case "": 225 | Usage() 226 | break 227 | default: 228 | fmt.Fprintln(os.Stderr, "Invalid function ", cmd) 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /arpthrifttest/arp.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 | -------------------------------------------------------------------------------- /arpthrifttest/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "arpthrift" 6 | "git.apache.org/thrift.git/lib/go/thrift" 7 | ) 8 | 9 | func main() { 10 | transport, err := thrift.NewTServerSocket("localhost:9091") 11 | 12 | if err != nil { 13 | fmt.Println("Error opening socket:", err) 14 | return 15 | } 16 | 17 | defer transport.Close() 18 | if err := transport.Open(); err != nil { 19 | fmt.Println("Error opening transport", err) 20 | return 21 | } 22 | 23 | handler := NewArpHandler() 24 | processor := arp.NewArpResponderProcessor(handler) 25 | server := thrift.NewTSimpleServer2(processor, transport) 26 | 27 | fmt.Println("Starting server...") 28 | server.Serve() 29 | } 30 | 31 | type ArpHandler struct { 32 | } 33 | 34 | func NewArpHandler() *ArpHandler { 35 | return &ArpHandler{} 36 | } 37 | 38 | func (p *ArpHandler) AddInterface(iface_name string) (r bool, err error) { 39 | fmt.Printf("add_interface(%v)\n", iface_name) 40 | r = true 41 | return 42 | } 43 | 44 | func (p *ArpHandler) DelInterface(iface_name string) (r bool, err error) { 45 | fmt.Printf("del_interface(%v)\n", iface_name) 46 | r = true 47 | return 48 | } 49 | 50 | func (p *ArpHandler) AddIP(iface_name string, stag arp.VlanTagT, ctag arp.VlanTagT, ip arp.Ip4T) (r bool, err error) { 51 | fmt.Printf("add_ip(%v, %+v, %v, %v)\n", iface_name, stag, ctag, ip) 52 | r = true 53 | return 54 | } 55 | 56 | func (p *ArpHandler) DelIP(iface_name string, stag arp.VlanTagT, ctag arp.VlanTagT) (r bool, err error) { 57 | fmt.Printf("del_ip(%v, %+v, %+v)\n", iface_name, stag, ctag) 58 | r = true 59 | return 60 | } 61 | 62 | func (p *ArpHandler) RequestMac(requests []*arp.ReqTuplesT) (r []*arp.RepTupleT, err error) { 63 | fmt.Printf("request_mac(%v)\n", requests) 64 | 65 | for _, req := range requests { 66 | if req.IP == 203569230 { 67 | r = append(r, &arp.RepTupleT{ 68 | Request: req.Tuples[int(req.Index) % len(req.Tuples)], 69 | Index: req.Index, 70 | Mac: arp.MacT([]byte{0x12, 0x34, 0x56, 0x78, 0x90, 0x12}), 71 | IsFound: true, 72 | }) 73 | } else if req.IP == 1510744632 { 74 | r = append(r, &arp.RepTupleT{ 75 | Request: req.Tuples[int(req.Index) % len(req.Tuples)], 76 | Index: req.Index, 77 | Mac: arp.MacT([]byte{0x34, 0x56, 0x78, 0x90, 0x12, 0x34}), 78 | IsFound: true, 79 | }) 80 | } 81 | } 82 | 83 | return 84 | } 85 | -------------------------------------------------------------------------------- /azurepipeline.yml: -------------------------------------------------------------------------------- 1 | trigger: 2 | branches: 3 | include: 4 | - "*" 5 | pr: 6 | branches: 7 | include: 8 | - "*" 9 | 10 | jobs: 11 | - job: Build 12 | pool: 13 | vmImage: ubuntu-20.04 14 | 15 | variables: 16 | DIFF_COVER_CHECK_THRESHOLD: 80 17 | DIFF_COVER_ENABLE: 'true' 18 | DIFF_COVER_WORKING_DIRECTORY: $(System.DefaultWorkingDirectory) 19 | 20 | steps: 21 | - checkout: self 22 | clean: true 23 | 24 | - script: | 25 | set -ex 26 | ./build.sh 27 | docker run -d --rm -p8090:8090 -p6379:6379 --name rest-api --cap-add NET_ADMIN --privileged -t rest-api-image-test_local:latest 28 | docker save rest-api-image-test_local | gzip > rest-api-image-test_local.gz 29 | docker save rest-api-build-image | gzip > rest-api-build-image.gz 30 | cp *.gz $(Build.ArtifactStagingDirectory) 31 | mkdir $(Build.ArtifactStagingDirectory)/debs 32 | cp debs/*.deb $(Build.ArtifactStagingDirectory)/debs 33 | cp debian/sonic-rest-api/usr/sbin/go-server-server $(Build.ArtifactStagingDirectory) 34 | displayName: 'Build image' 35 | 36 | - script: | 37 | set -ex 38 | pip install -r test/requirements.txt 39 | pip install -U pytest 40 | cd test 41 | pytest -vv 42 | docker exec rest-api cat /tmp/rest-api.err.log 43 | docker exec rest-api ps aux 44 | sleep 600 45 | cd .. 46 | docker cp rest-api:/coverage.cov ./ 47 | docker run -d --rm=true --name rest-api_build -v /tmp/target:/src -v $(pwd):/src/rest -w /src/rest rest-api-build-image tail -f /dev/null 48 | mkdir htmlcov 49 | docker exec rest-api_build sh /src/rest/cover.sh 50 | displayName: Test 51 | 52 | - publish: $(Build.ArtifactStagingDirectory)/ 53 | artifact: 'sonic-restapi' 54 | displayName: "Archive artifacts" 55 | 56 | - script: | 57 | set -ex 58 | # Install .NET CORE 59 | curl -sSL https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add - 60 | sudo apt-add-repository https://packages.microsoft.com/ubuntu/20.04/prod 61 | sudo apt-get update 62 | sudo apt-get install -y dotnet-sdk-5.0 63 | displayName: "Install .NET CORE" 64 | 65 | - task: PublishCodeCoverageResults@1 66 | inputs: 67 | codeCoverageTool: Cobertura 68 | summaryFileLocation: '$(System.DefaultWorkingDirectory)/coverage.xml' 69 | reportDirectory: '$(System.DefaultWorkingDirectory)/htmlcov/' 70 | displayName: 'Publish test coverage' 71 | -------------------------------------------------------------------------------- /build.md: -------------------------------------------------------------------------------- 1 | # How to build the Go MSEE server 2 | 3 | ## Prerequesites 4 | 1) libswsscommon built and the library available on `LD_LIBRARY_PATH`. Can be found at [sonic-swss-common](https://github.com/Azure/sonic-swss-common) - follow the build instructions there. Must also set `SWSSCOMMON_SRC` to the full path to the directory `sonic-swss-common`. 5 | 2) libcswsscommon must be built, run make within `MSEE/libcswsscommon` after step 1. 6 | 2) Redis database on localhost:6379 - we use DB 0 (`APPL_DB`) and 4 7 | 3) Go version 1.8 (static binaries can be found at [https://golang.org/dl/](https://golang.org/dl/)) 8 | 4) Thrift to build mock server - Ubuntu will need to build from source [https://g4greetz.wordpress.com/2016/10/29/how-to-install-apache-thrift-in-ubuntu/](https://g4greetz.wordpress.com/2016/10/29/how-to-install-apache-thrift-in-ubuntu/) - you may also need to add `/usr/local/lib` to `LD_LIBRARY_PATH` 9 | 5) Python (2 or 3) with python-redis for the tests 10 | 6) The following variables must be set 11 | ``` 12 | CGO_CFLAGS="-I(PATH_TO_NETWORKING_ACS_RESTAPI)/MSEE/libcswsscommon/include" 13 | 14 | CGO_LDFLAGS="-L(PATH_TO_NETWORKING_ACS_RESTAPI)/MSEE/libcswsscommon -L(PATH_TO_LIBSWSSCOMMON_INSTALL)/lib" 15 | 16 | LD_LIBRARY_PATH="(PATH_TO_NETWORKING_ACS_RESTAPI)/MSEE/libcswsscommon:(PATH_TO_LIBSWSSCOMMON_INSTALL)/lib" 17 | ``` 18 | 19 | ## Go environment 20 | The directories within the MSEE repo should be symlinked so that they follow the proper Go layout. 21 | 22 | ``` 23 | $GOPATH/src/go-server-server -> MSEE/go-server-server 24 | $GOPATH/src/mseethrift -> MSEE/mseethrift 25 | $GOPATH/src/mseethrifttest -> MSEE/mseethrifttest 26 | $GOPATH/src/swsscommon -> MSEE/swsscommon 27 | ``` 28 | 29 | It should then be possible to run `go get` followed by `go build` within `$GOPATH/src/go-server-server` to build the `go-server-server` binary. 30 | 31 | ## Building the mock Thrift server 32 | The Go server requires that it can connect to the Thrift server, we have a mock server that will allow every request to suceed, this will allow the Go server to run successfully. 33 | 34 | To run the mock Thrift server run `go run server.go` within `$GOPATH/src/mseethrifttest` 35 | 36 | ## Command line arguments 37 | The `go-server-server` binary supports several command line arguments. 38 | ``` 39 | -h - Lists command line arguments 40 | -reset - Resets the cache DB (DB 4) 41 | -macaddr= - Specifies switch MAC address (required) 42 | -loaddr= - Specified switch loopback address (required) 43 | -thrift= - Specifies the host and port of the Thrift server, defaults to localhost:9090 44 | ``` 45 | 46 | ## Running tests 47 | In order to run the tests the Redis DB and mock Thrift server must be running. Once they are both running, start the `go-server-server` binary. 48 | 49 | In another terminal run `python -m unittest apitest` within the `MSEE/test` directory to run the tests. Some tests will be skipped or expected to fail, this is expected due to the incomplete API at this time. There should be no unexpected failures. 50 | 51 | If there are unexpected failures ensure that the Redis DB and mock Thrift servers are running. 52 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | 2 | set -e 3 | 4 | # copy debian packages from sonic-buildimage 5 | #bash copy.sh 6 | python3 copy.py 7 | 8 | # build a container with build utilities 9 | docker build -t rest-api-build-image --rm -f Dockerfile.build . 10 | 11 | # create a directory for target .deb files 12 | mkdir -p /tmp/target 13 | 14 | # create .deb packages inside of the build container 15 | docker run --rm=true -v /tmp/target:/src -v $(pwd):/src/rest -w /src/rest rest-api-build-image dpkg-buildpackage -us -uc 16 | #docker run --rm=true -v /tmp/target:/src -v $(pwd)/arp_responder:/src/arp -w /src/arp rest-api-build-image dpkg-buildpackage -us -uc 17 | 18 | # copy created packages to out debs directoy 19 | cp /tmp/target/*.deb debs 20 | 21 | # remove cruft 22 | sudo rm -fr /tmp/target 23 | 24 | # build rest-api test image 25 | docker build -t rest-api-image-test_local --rm -f Dockerfile.test . 26 | 27 | # build rest-api production image 28 | # docker build -t rest-api-image --rm . 29 | 30 | 31 | # save rest-api-image into a file 32 | # docker save rest-api-image | gzip > rest-api-image.gz 33 | -------------------------------------------------------------------------------- /cert/client/selfsigned.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDLDCCAhSgAwIBAgIRALJUpmQD3BOrSPH1HmX04nwwDQYJKoZIhvcNAQELBQAw 3 | LDEUMBIGA1UEChMLTG9nIENvdXJpZXIxFDASBgNVBAMTC1NvbmljQ0xpZW50MB4X 4 | DTE3MDkyNjAwMjczMloXDTI3MDkyNDAwMjczMlowLDEUMBIGA1UEChMLTG9nIENv 5 | dXJpZXIxFDASBgNVBAMTC1NvbmljQ0xpZW50MIIBIjANBgkqhkiG9w0BAQEFAAOC 6 | AQ8AMIIBCgKCAQEA2Pog1Bdc6dEQzC7LSNvqqDe15rLhzIBEkOsptlvC2kAYrWN5 7 | iilUjHO9F0UJoR2urdN6wmfqisq4IiHS9hLZhwCnCZEh1Jf+Q5Go1v2yF2eRG6Mw 8 | zfuhYKR1xTI74EU+r2wpovoIUVmtcUNImq+lSjU3yqarfsxDiqEjt678l5K7IMvg 9 | S6OdCRP/COGDeSDl8+E9tnPNCuehZkaxWndDhhF3kys6Uylb94CVEz3w0YksOv3p 10 | v66DVATvhosr2Ss3omEfSM3y2nFT6GnB81eX2XfBRErZgWlMx4OXnWz3m3nFm5uG 11 | PMjhiy9VJnWrJ+4UGuZMVszvMhNIouuv+DsQlwIDAQABo0kwRzAOBgNVHQ8BAf8E 12 | BAMCAqQwEwYDVR0lBAwwCgYIKwYBBQUHAwIwDwYDVR0TAQH/BAUwAwEB/zAPBgNV 13 | HREECDAGhwR/AAABMA0GCSqGSIb3DQEBCwUAA4IBAQB0UHNQrRv6a3Y6rtx45gH5 14 | mcA36zG/ucyyMGKrdnlDfRzUrsU6vM7hSzAqrwbhNv+G+dRVnYUhv2iwCxVMfZM3 15 | 1UMMEysyJGxdi7O628nZBEkwHijqVB6jUNvDnZD2OKEiLxmr98d5eLUt7ZqASQX3 16 | tYZJMkRYEbQNq5tH0M2ToNu2QRzqbclYhLQZ0UPkMU4ggpWPQLUfft3be0Hmpjki 17 | 2J4zIZpBRh7uvDmQST7Wx5w34W+18SXny6tF0F6AcTpakgXAVUuVuhxjfz3iovyk 18 | rLu7yDQdxu2mZArdO0tzDYR8n2TEaPcuN8BZWNRPkdFz5jJ1GuCbYX2Wt+tKTJON 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /cert/client/selfsigned.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEA2Pog1Bdc6dEQzC7LSNvqqDe15rLhzIBEkOsptlvC2kAYrWN5 3 | iilUjHO9F0UJoR2urdN6wmfqisq4IiHS9hLZhwCnCZEh1Jf+Q5Go1v2yF2eRG6Mw 4 | zfuhYKR1xTI74EU+r2wpovoIUVmtcUNImq+lSjU3yqarfsxDiqEjt678l5K7IMvg 5 | S6OdCRP/COGDeSDl8+E9tnPNCuehZkaxWndDhhF3kys6Uylb94CVEz3w0YksOv3p 6 | v66DVATvhosr2Ss3omEfSM3y2nFT6GnB81eX2XfBRErZgWlMx4OXnWz3m3nFm5uG 7 | PMjhiy9VJnWrJ+4UGuZMVszvMhNIouuv+DsQlwIDAQABAoIBAQDKweKPj016Ca4C 8 | v++jJC7CB11qOdsbOPnZ2odeK52IScFFteFLpJensDgRWKhI8O3c7fSXWngoPouX 9 | CbiTzsWLjKaLIB5XBbnK9oCqduRHTrag9vySYARLxSNwpSKiztJnJ1I5RCEltAGK 10 | B+/+7QguslByhRFZKhQrpa005saP9At5rOJY2FSToONU9mtZsV9FZ7rMpdVtNzeA 11 | 0hOKxiOhTMib4xnrXaj38aql4r2q8K/y2oFX1nGvcITvwEIzwvxpxY9yw9kykccF 12 | 0dCqvcS2JuNQExxWi0GVNIIWzAWpsjBvduYomBd8zaqMN86jCJgUVvQbHHavC0FK 13 | KfYZe5AxAoGBAO696wLhRm1ayzdW81gAcGY/tiuGmVY9TaL2Lz7gd1W8+/zAGXKP 14 | DGoaH1GLulahf4LvBocGkJsxmncXPVPAs7yDVHx+sI5+pO/7MwWxo2r5Mjz1b0oN 15 | trGRJ1qd5nf+NIB42e/JKvG3yY11MN/msGZtVDO74v9QyHLdzhq2SF09AoGBAOip 16 | cAazHL9nCCr3MBsijxIHyrnzdMLRmFXnJMfBlOyKyi3dh4TXEUwqbxbWUhHMqFxX 17 | oqlqihuCQdrK088zPV4xCnC+WLMxubTynpFmSVu8WV/trzUWUKpGj+hg2vGXVd1a 18 | Tf3jF4DUvR9gIXoBlKVOM2F00EwDKPl6qX277ypjAoGBAKeEST+HFGSs3YSiFTN9 19 | c2C7ebaGxlb1wuECvtnupK1bYrDRlcmFSB+23pzYX1JkFVWpJDpgR90tbAX+H4EB 20 | 6NvwhrqoJniFsnPVHdLz9webfT26tXEPuE5V3Hg/7TPrAanXoowW6nQbEmiYf0fL 21 | 1aC5O+jk//1IEK5Qpkql9pa9AoGAOHBx5o4omR3LU3DnUI3PmxYczYMLff8OLziF 22 | KZgCImeAqCOnLkhlv85DaIzRF9RHuy7fHK3llsrAYuz0+Lx7VJAc8kCaj82uOQDq 23 | Cmd/wCsumyCzcfNZ2hSH8F0ef6CwiaPQa5hdKTlyL1xIgqpDft47hJaruOTDWdGx 24 | r/iZCfUCgYAV7HImzuU5sU5cr7O+RGFm2c1SdRTmnGPhMg3uqMu26kEGRhfqUy1l 25 | mx3B7l9F9wSfDwvY8uwZcdvo8SLqGNnGVs2E0Gd5EMneeC5uCDl2U7PswoTn8nrh 26 | 7N9looXDrdUXpbgsESByEFechxQuy8oTeCJNTiPV/TBjQonU9iMPAA== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /cert/server/selfsigned.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDJzCCAg+gAwIBAgIQNfqjmTbQctMKhnTF5opO+zANBgkqhkiG9w0BAQsFADAq 3 | MRQwEgYDVQQKEwtMb2cgQ291cmllcjESMBAGA1UEAxMJbG9jYWxob3N0MB4XDTE3 4 | MDkyNjAwMjI0NVoXDTI3MDkyNDAwMjI0NVowKjEUMBIGA1UEChMLTG9nIENvdXJp 5 | ZXIxEjAQBgNVBAMTCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC 6 | AQoCggEBANcr0I/c1m38DIhU5Di1XvhYC+FmtbpkVLZ78J2k8TO/83Z/XSwwZwb9 7 | wNrIphhYOaaUedURDFzhOpde6TtyHTPAbyNUbU3Mh9BHfBU6ztiVY4MWS3Pk0yfh 8 | wVededPA+qo565jM4HmGJExHyQDtcTe/ER+3TGjztWRiNVSTf+pRG4eRSWrWulvp 9 | HJzOvb7Ne4VLqCrM5rN9oTEtLX1XOMhJFc9mfR/w1q/TYF7VWHWcYzqH1uvFeVrO 10 | naj3nkd7fCVpiZLApWkeUBM89tWP2c3nroaYMmLb8oxFlYZwzJBBVPkoFxjtqLEo 11 | iftob1o4s9XCR3h2YcahPb5o6MZe6m8CAwEAAaNJMEcwDgYDVR0PAQH/BAQDAgKk 12 | MBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMBAf8wDwYDVR0RBAgw 13 | BocEfwAAATANBgkqhkiG9w0BAQsFAAOCAQEAMQxSNIuIcfhU7VzvP0ago4+Er0qR 14 | U3B1pNQ1K+3TILHe13OBS/EJpqFJ3XjsEjHafXQBmgCwumx4a3TENkimKB3jA3MB 15 | /r8iGp9ORHHeNfGw/fD+UVXOjczkDlM+sPeeKY8MkpJYajzFBiHsYU9jnkSjvon1 16 | UKbnuUKBycxhVC5ZQ1TRYeWgaQq66Si1unVHjsqVgmme2qDJJ01FcaDbjqsdgR0a 17 | tXjRr4oRip9P1fmKX1mcC3SntmuZHDgdO29ZYFTE+LLTsNQceZnLOtj/3n9wIaFn 18 | WWcqJ5dJ4VrfXch6Lkp3NW06LEYsXK4eMe0PXZVWN9gbyD5zBS+QnVAFag== 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /cert/server/selfsigned.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEA1yvQj9zWbfwMiFTkOLVe+FgL4Wa1umRUtnvwnaTxM7/zdn9d 3 | LDBnBv3A2simGFg5ppR51REMXOE6l17pO3IdM8BvI1RtTcyH0Ed8FTrO2JVjgxZL 4 | c+TTJ+HBV51508D6qjnrmMzgeYYkTEfJAO1xN78RH7dMaPO1ZGI1VJN/6lEbh5FJ 5 | ata6W+kcnM69vs17hUuoKszms32hMS0tfVc4yEkVz2Z9H/DWr9NgXtVYdZxjOofW 6 | 68V5Ws6dqPeeR3t8JWmJksClaR5QEzz21Y/ZzeeuhpgyYtvyjEWVhnDMkEFU+SgX 7 | GO2osSiJ+2hvWjiz1cJHeHZhxqE9vmjoxl7qbwIDAQABAoIBAQCSJFAjZfq9+NMK 8 | RZ48ZfN6+VIYZYpIbpya1NM4Py8T1uulMBw4Ryeyt+aksmMdZc7CNVYKQOoEq3UA 9 | GpaOqZLr82eIt28FyET2UZcVVDvpHo3YGuxkaqaAD/UhbPOxvwCBXZWG0qAj6df9 10 | d0QuPRcIpd0bjYN4RQ6Of6V5LDVVD0IXStKC4j5Pzx/I0kUpYAiuqsk8/mOzsTad 11 | BcH+62m5ydKgqc5K+pIthfkvqH3A0tJePn3hzcK1rs1hstK/WyI06DVf5aUS5Ai7 12 | 2HRsI/a1YUmRZ8o+dv0Pt3p359M5uHLCtoiWC5OqI8S9vbYXZDgO57NcNAYIMspo 13 | yko/D+4BAoGBANynpzMRTGlCEPjwY/zInAWQo+aSb9Xfb+fxvKmC0J2JNQmIgxXS 14 | ZWqIzIGk7Ibs1fqRZVhidsOpr08F250eVz7UWZerN/wwzG+119yTq/azVeZmQaGw 15 | J5AmpToe+GaZyS0L+VKSa19c/10LjyWazHRVebshZHBL8rA3bzhggxlHAoGBAPmj 16 | SnfP8V9aLl1TDbumSjPM+YRD2F5jqzPruNlYrWOi5lWmDsBCRGY3c5lnFOk4zmVv 17 | EKom9odLP1ADura0Mje0BBtzbt3mAF8rVELnYtvHzCDpBrd+nTiUzn4ZbI5COhAH 18 | cm30Q9dIbnke8vCNB04dRWgPzdXlGTBtpI2PfDmZAoGAL4YAUdRg866h/yuvLUt7 19 | pwsu8cMKSTL1ZCxBaPTev39EqAf5y6Jj610JRsbhlnG96FiWTkHuuFd8hXWKhj15 20 | pvOPVEYxKaTM8yZkFoLLn7eiQZw2eH97FeXs4ia3c0md9ZDFow4cCA78hOqvavb7 21 | H/ck37PicPk6jM9zqd3HfpMCgYEA40VPWa7dz/KZPWuMmCc3bNuS2mCuDpjs17Mm 22 | MfHywUAgNR+eVFjATtLzEljvh7io513rLQ2V6eyOsnkCtq1s/puMFbXM3IY2Fm6r 23 | ItHnhbHrXJbp/4ps9oM7T0A0PXt7wvYUrpfwX4HY2bpccLXkTbCGbMvemHofqaBY 24 | bGySKxkCgYBQ/Pv9cDgyKGrXr9SSQtcwqdAUXb0BuPbaH2zQN4M4x5IKjsb9494/ 25 | DQ6ecbQZ1fetOFnon0XRQmWVm3A6Vl6/+H8Ghgxgb94Dn4TcLi4Cma9W0KbD4DaF 26 | 6enFVK36B+vkDh2iCsbwxjh5Gt3YcjJehvyzMBgm8Jo40H6SDWosnQ== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /copy.py: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/python3 2 | 3 | import json 4 | import os 5 | import urllib.parse 6 | from subprocess import Popen, PIPE 7 | 8 | URL = "https://sonic-build.azurewebsites.net/api/sonic/artifacts" 9 | CONF_FILE = "dependencies.conf" 10 | 11 | def main(): 12 | conf_file_path = os.path.join(CONF_FILE) 13 | conf_t = open(conf_file_path).read() 14 | config = json.loads(conf_t) 15 | 16 | if not os.path.exists("./debs/"): 17 | os.mkdir("./debs") 18 | 19 | for dep in config['dependencies']: 20 | params = { 21 | 'branchName': config['branch'], 22 | 'platform': config['platform'], 23 | 'target': "target/debs/"+config['debian']+"/"+dep+"_"+config['arch']+".deb" 24 | } 25 | url = URL+"?"+urllib.parse.urlencode(params) 26 | download_cmd = "wget -O debs/"+dep+"_"+config['arch']+".deb "+url 27 | print(download_cmd) 28 | process = Popen(download_cmd.split(), stdout=PIPE, stderr=PIPE) 29 | process.communicate() 30 | 31 | if __name__ == "__main__": 32 | main() 33 | -------------------------------------------------------------------------------- /copy.sh: -------------------------------------------------------------------------------- 1 | set -e 2 | 3 | wget -O debs/libhiredis0.14_0.14.1-1_amd64.deb 'https://sonic-build.azurewebsites.net/api/sonic/artifacts?branchName=master&platform=vs&target=target%2Fdebs%2Fbuster%2Flibhiredis0.14_0.14.1-1_amd64.deb' 4 | wget -O debs/libhiredis-dev_0.14.1-1_amd64.deb 'https://sonic-build.azurewebsites.net/api/sonic/artifacts?branchName=master&platform=vs&target=target%2Fdebs%2Fbuster%2Flibhiredis-dev_0.14.1-1_amd64.deb' 5 | wget -O debs/libnl-3-200_3.5.0-1_amd64.deb 'https://sonic-build.azurewebsites.net/api/sonic/artifacts?branchName=master&platform=vs&target=target/debs/buster/libnl-3-200_3.5.0-1_amd64.deb' 6 | wget -O debs/libnl-3-dev_3.5.0-1_amd64.deb 'https://sonic-build.azurewebsites.net/api/sonic/artifacts?branchName=master&platform=vs&target=target/debs/buster/libnl-3-dev_3.5.0-1_amd64.deb' 7 | wget -O debs/libnl-genl-3-200_3.5.0-1_amd64.deb 'https://sonic-build.azurewebsites.net/api/sonic/artifacts?branchName=master&platform=vs&target=target/debs/buster/libnl-genl-3-200_3.5.0-1_amd64.deb' 8 | wget -O debs/libnl-route-3-200_3.5.0-1_amd64.deb 'https://sonic-build.azurewebsites.net/api/sonic/artifacts?branchName=master&platform=vs&target=target/debs/buster/libnl-route-3-200_3.5.0-1_amd64.deb' 9 | wget -O debs/libnl-nf-3-200_3.5.0-1_amd64.deb 'https://sonic-build.azurewebsites.net/api/sonic/artifacts?branchName=master&platform=vs&target=target/debs/buster/libnl-nf-3-200_3.5.0-1_amd64.deb' 10 | wget -O debs/libthrift-0.11.0_0.11.0-4_amd64.deb 'https://sonic-build.azurewebsites.net/api/sonic/artifacts?branchName=master&platform=vs&target=target/debs/buster/libthrift-0.11.0_0.11.0-4_amd64.deb' 11 | wget -O debs/libyang_1.0.73_amd64.deb 'https://sonic-build.azurewebsites.net/api/sonic/artifacts?branchName=master&platform=vs&target=target%2Fdebs%2Fbuster%2Flibyang_1.0.73_amd64.deb' 12 | wget -O debs/libswsscommon_1.0.0_amd64.deb 'https://sonic-build.azurewebsites.net/api/sonic/artifacts?branchName=master&platform=vs&target=target%2Fdebs%2Fbuster%2Flibswsscommon_1.0.0_amd64.deb' 13 | wget -O debs/libswsscommon-dev_1.0.0_amd64.deb 'https://sonic-build.azurewebsites.net/api/sonic/artifacts?branchName=master&platform=vs&target=target%2Fdebs%2Fbuster%2Flibswsscommon-dev_1.0.0_amd64.deb' 14 | -------------------------------------------------------------------------------- /cover.sh: -------------------------------------------------------------------------------- 1 | export GOROOT=/usr/local/go 2 | export GOPATH=$HOME/go 3 | 4 | mkdir -p $GOPATH/src 5 | cp -r go-server-server $GOPATH/src/go-server-server 6 | 7 | go tool cover -html=coverage.cov -o=htmlcov/coverage.html 8 | 9 | go get github.com/t-yuki/gocover-cobertura 10 | $GOPATH/bin/gocover-cobertura < coverage.cov > coverage.xml 11 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | sonic-rest-api-server (1.0.1) stable; urgency=medium 2 | 3 | * VNet/Vxlan support. 4 | 5 | -- Prince Sunny Mon, 8 Jul 2019 15:00:15 -0800 6 | 7 | sonic-rest-api-server (1.0.0) stable; urgency=medium 8 | 9 | * Initial release. 10 | 11 | -- Pavel Shirshov Wed, 19 Jul 2017 12:30:15 -0800 12 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 9 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: sonic-rest-api-server 2 | Maintainer: Pavel Shirshov 3 | Section: net 4 | Priority: optional 5 | Build-Depends: dh-exec (>=0.3), debhelper (>= 9) 6 | Standards-Version: 1.0.0 7 | 8 | Package: sonic-rest-api 9 | Architecture: any 10 | Depends: ${shlibs:Depends} 11 | Description: This package contains rest api server for MS EE and Baremetal scenarios 12 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # See debhelper(7) (uncomment to enable) 3 | # output every command that modifies files on the build system. 4 | #export DH_VERBOSE = 1 5 | 6 | # see EXAMPLES in dpkg-buildflags(1) and read /usr/share/dpkg/* 7 | DPKG_EXPORT_BUILDFLAGS = 1 8 | include /usr/share/dpkg/default.mk 9 | 10 | # see FEATURE AREAS in dpkg-buildflags(1) 11 | #export DEB_BUILD_MAINT_OPTIONS = hardening=+all 12 | 13 | # see ENVIRONMENT in dpkg-buildflags(1) 14 | # package maintainers to append CFLAGS 15 | #export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic 16 | # package maintainers to append LDFLAGS 17 | #export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed 18 | 19 | 20 | # main packaging script based on dh7 syntax 21 | %: 22 | dh $@ --with autotools-dev 23 | 24 | # dh_make generated override targets 25 | # This is example for Cmake (See https://bugs.debian.org/641051 ) 26 | #override_dh_auto_configure: 27 | # dh_auto_configure -- \ 28 | # -DCMAKE_LIBRARY_PATH=$(DEB_HOST_MULTIARCH) 29 | 30 | override_dh_auto_configure: 31 | dh_auto_configure -- 32 | 33 | override_dh_auto_install: 34 | dh_auto_install --destdir=debian/sonic-rest-api 35 | -------------------------------------------------------------------------------- /debian/sonic-rest-api.dirs: -------------------------------------------------------------------------------- 1 | /usr/sbin 2 | -------------------------------------------------------------------------------- /debs/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonic-net/sonic-restapi/4a564baddf7abe8fb904ed9fbb4f713954a84573/debs/.keep -------------------------------------------------------------------------------- /debug/show_encap_tunnels: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | curl -X GET http://localhost:8080/v1/config/tunnel/encap/vxlan 3 | -------------------------------------------------------------------------------- /debug/show_interface: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | curl -X GET http://localhost:8080/v1/config/interface/$1/$2 3 | -------------------------------------------------------------------------------- /debug/show_routes: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | curl -X GET http://localhost:8080/v1/config/vrouter/$1/routes 3 | -------------------------------------------------------------------------------- /debug/show_vrouters: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | curl -X GET http://localhost:8080/v1/config/vrouter 3 | -------------------------------------------------------------------------------- /dependencies.conf: -------------------------------------------------------------------------------- 1 | { 2 | "branch": "master", 3 | "platform": "vs", 4 | "debian": "buster", 5 | "arch": "amd64", 6 | "dependencies": [ 7 | "libnl-3-200_3.5.0-1", 8 | "libnl-3-dev_3.5.0-1", 9 | "libnl-genl-3-200_3.5.0-1", 10 | "libnl-route-3-200_3.5.0-1", 11 | "libnl-nf-3-200_3.5.0-1", 12 | "libyang_1.0.73", 13 | "libswsscommon_1.0.0", 14 | "libswsscommon-dev_1.0.0" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /files/server_kill.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os, signal 4 | from subprocess import check_output 5 | import time 6 | 7 | time.sleep(10*60) 8 | 9 | pid = int(check_output(["pidof", "go-server-server.test"])) 10 | os.kill(pid, signal.SIGQUIT) 11 | -------------------------------------------------------------------------------- /go-server-server/.swagger-codegen-ignore: -------------------------------------------------------------------------------- 1 | # Swagger Codegen Ignore 2 | # Generated by swagger-codegen https://github.com/swagger-api/swagger-codegen 3 | 4 | # Use this file to prevent files from being overwritten by the generator. 5 | # The patterns follow closely to .gitignore or .dockerignore. 6 | 7 | # As an example, the C# client generator defines ApiClient.cs. 8 | # You can make changes and tell Swagger Codgen to ignore just this file by uncommenting the following line: 9 | #ApiClient.cs 10 | 11 | # You can match any string of characters against a directory, file or extension with a single asterisk (*): 12 | #foo/*/qux 13 | # The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux 14 | 15 | # You can recursively match patterns against a directory, file or extension with a double asterisk (**): 16 | #foo/**/qux 17 | # This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux 18 | 19 | # You can also negate patterns with an exclamation (!). 20 | # For example, you can ignore all files in a docs folder with the file extension .md: 21 | #docs/*.md 22 | # Then explicitly reverse the ignore rule for a single file: 23 | #!docs/README.md 24 | -------------------------------------------------------------------------------- /go-server-server/go.mod: -------------------------------------------------------------------------------- 1 | module go-server-server 2 | 3 | go 1.15 4 | 5 | require ( 6 | github.com/comail/colog v0.0.0-20160416085026-fba8e7b1f46c 7 | github.com/go-redis/redis/v7 v7.3.0 8 | github.com/gorilla/mux v1.7.4 9 | github.com/satori/go.uuid v1.2.1-0.20180404165556-75cca531ea76 10 | github.com/vharitonsky/iniflags v0.0.0-20180513140207-a33cd0b5f3de 11 | swsscommon v0.0.0 12 | ) 13 | 14 | replace swsscommon v0.0.0 => ../swsscommon 15 | -------------------------------------------------------------------------------- /go-server-server/go.sum: -------------------------------------------------------------------------------- 1 | github.com/comail/colog v0.0.0-20160416085026-fba8e7b1f46c h1:bzYQ6WpR+t35/y19HUkolcg7SYeWZ15IclC9Z4naGHI= 2 | github.com/comail/colog v0.0.0-20160416085026-fba8e7b1f46c/go.mod h1:1WwgAwMKQLYG5I2FBhpVx94YTOAuB2W59IZ7REjSE6Y= 3 | github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= 4 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 5 | github.com/go-redis/redis/v7 v7.3.0 h1:3oHqd0W7f/VLKBxeYTEpqdMUsmMectngjM9OtoRoIgg= 6 | github.com/go-redis/redis/v7 v7.3.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg= 7 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 8 | github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= 9 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 10 | github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc= 11 | github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= 12 | github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= 13 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 14 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 15 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 16 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 17 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 18 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 19 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 20 | github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo= 21 | github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 22 | github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= 23 | github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 24 | github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= 25 | github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= 26 | github.com/satori/go.uuid v1.2.1-0.20180404165556-75cca531ea76 h1:ofyVTM1w4iyKwaQIlRR6Ip06mXXx5Cnz7a4mTGYq1hE= 27 | github.com/satori/go.uuid v1.2.1-0.20180404165556-75cca531ea76/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= 28 | github.com/vharitonsky/iniflags v0.0.0-20180513140207-a33cd0b5f3de h1:fkw+7JkxF3U1GzQoX9h69Wvtvxajo5Rbzy6+YMMzPIg= 29 | github.com/vharitonsky/iniflags v0.0.0-20180513140207-a33cd0b5f3de/go.mod h1:irMhzlTz8+fVFj6CH2AN2i+WI5S6wWFtK3MBCIxIpyI= 30 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 31 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 32 | golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g= 33 | golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 34 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 35 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 36 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 37 | golang.org/x/sys v0.0.0-20191010194322-b09406accb47 h1:/XfQ9z7ib8eEJX2hdgFTZJ/ntt0swNk5oYBziWeTCvY= 38 | golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 39 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 40 | golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= 41 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 42 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 43 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 44 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= 45 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 46 | gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= 47 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 48 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= 49 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 50 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 51 | gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= 52 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 53 | -------------------------------------------------------------------------------- /go-server-server/go/auth.go: -------------------------------------------------------------------------------- 1 | package restapi 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | ) 7 | 8 | func CommonNameMatch(r *http.Request) bool { 9 | //FIXME : in the authentication of client certificate, after the certificate chain is validated by 10 | // TLS, here we will futher check if the common name of the end-entity certificate is in the trusted 11 | // common name list of the server config. A more strict check may be added here later. 12 | for _, peercert := range r.TLS.PeerCertificates { 13 | commonName := peercert.Subject.CommonName 14 | for _, name := range trustedCertCommonNames { 15 | if commonName == name { 16 | return true; 17 | } 18 | } 19 | log.Printf("info: CommonName in the client cert: %s", commonName) 20 | } 21 | 22 | log.Printf("error: Authentication Fail! None of the CommonNames in the client cert are found in trusted common names") 23 | return false; 24 | } -------------------------------------------------------------------------------- /go-server-server/go/flags.go: -------------------------------------------------------------------------------- 1 | package restapi 2 | 3 | import "flag" 4 | 5 | var LogLevelFlag = flag.String("loglevel", "info", "Set's minimum log level, valid values are: trace, debug, info, warning, error, alert") 6 | var LogFileFlag = flag.String("logfile", "/dev/stderr", "Set's the output for the log") 7 | var HttpFlag = flag.Bool("enablehttp", true, "Enable http endpoint") 8 | var HttpsFlag = flag.Bool("enablehttps", false, "Enable https endpoint") 9 | var ClientCertFlag = flag.String("clientcert", "", "Client cert file") 10 | var ClientCertCommonNameFlag = flag.String("clientcertcommonname", "SonicCLient", "Comma separated list of trusted common names in the client cert file") 11 | var ServerCertFlag = flag.String("servercert", "", "Server cert file") 12 | var ServerKeyFlag = flag.String("serverkey", "", "Server key file") 13 | var RunApiAsLocalTestDocker = flag.Bool("localapitestdocker", false, "Defines whether Rest API is to be run as an independent test docker or with other SONiC components") 14 | var SystemTestFlag = flag.Bool("systemtest", false, "Set this flag if running system test") 15 | -------------------------------------------------------------------------------- /go-server-server/go/logger.go: -------------------------------------------------------------------------------- 1 | package restapi 2 | 3 | import ( 4 | "github.com/comail/colog" 5 | "log" 6 | "net/http" 7 | "os" 8 | ) 9 | 10 | type LoggingResponseWriter struct { 11 | inner http.ResponseWriter 12 | } 13 | 14 | func (w LoggingResponseWriter) Header() http.Header { 15 | return w.inner.Header() 16 | } 17 | 18 | func (w LoggingResponseWriter) Write(b []byte) (int, error) { 19 | return w.inner.Write(b) 20 | } 21 | 22 | func (w LoggingResponseWriter) WriteHeader(statusCode int) { 23 | log.Printf("info: request: return %d", statusCode) 24 | w.inner.WriteHeader(statusCode) 25 | } 26 | 27 | func NewLoggingResponseWriter(w http.ResponseWriter) LoggingResponseWriter { 28 | return LoggingResponseWriter{inner: w} 29 | } 30 | 31 | func InitLogging() { 32 | colog.Register() 33 | 34 | level, err := colog.ParseLevel(*LogLevelFlag) 35 | if err != nil { 36 | log.Fatalf("error: invalid minimum log level %s", *LogLevelFlag) 37 | } 38 | colog.SetMinLevel(level) 39 | 40 | file, err := os.OpenFile(*LogFileFlag, os.O_RDWR | os.O_CREATE | os.O_APPEND, 0666) 41 | if err != nil { 42 | log.Fatalf("error: couldn't open log file %s, %s", *LogFileFlag, err) 43 | } 44 | colog.SetOutput(file) 45 | } -------------------------------------------------------------------------------- /go-server-server/go/netstatus.go: -------------------------------------------------------------------------------- 1 | package restapi 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | "strings" 7 | ) 8 | 9 | func GetAllNetworkStatuses() (operstatuses map[string]bool, err error) { 10 | operstatuses = make(map[string]bool) 11 | 12 | fi, err := ioutil.ReadDir("/sys/class/net") 13 | if err != nil { 14 | return 15 | } 16 | 17 | for _, f := range fi { 18 | var ns bool 19 | ns, err = GetNetworkStatus(f.Name()) 20 | if err != nil { 21 | return 22 | } 23 | operstatuses[f.Name()] = ns 24 | } 25 | 26 | return 27 | } 28 | 29 | func GetNetworkStatus(port string) (operstatus bool, err error) { 30 | b, err := ioutil.ReadFile("/sys/class/net/" + port + "/operstate") 31 | if err != nil { 32 | return 33 | } 34 | 35 | if string(b) == "up\n" { 36 | operstatus = true 37 | } else { 38 | operstatus = false 39 | } 40 | 41 | return 42 | } 43 | 44 | func GetPorts(prefix string) (ports []string, err error) { 45 | fi, err := ioutil.ReadDir("/sys/class/net") 46 | if err != nil { 47 | return 48 | } 49 | 50 | for _, f := range fi { 51 | if strings.HasPrefix(f.Name(), prefix) { 52 | ports = append(ports, f.Name()) 53 | } 54 | } 55 | 56 | return 57 | } 58 | 59 | func PortExists(port string) bool { 60 | _, err := os.Stat("/sys/class/net/" + port) 61 | return err == nil 62 | } 63 | -------------------------------------------------------------------------------- /go-server-server/go/routers.go: -------------------------------------------------------------------------------- 1 | package restapi 2 | 3 | import ( 4 | "net/http" 5 | "fmt" 6 | "log" 7 | "time" 8 | "sync" 9 | "github.com/gorilla/mux" 10 | ) 11 | 12 | type Route struct { 13 | Name string 14 | Method string 15 | Pattern string 16 | HandlerFunc http.HandlerFunc 17 | } 18 | 19 | type Routes []Route 20 | 21 | var writeMutex sync.Mutex 22 | 23 | func Middleware(inner http.Handler, name string) http.Handler { 24 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 25 | start := time.Now() 26 | log.Printf( 27 | "info: request: %s %s %s", 28 | r.Method, 29 | r.RequestURI, 30 | name, 31 | ) 32 | 33 | if r.TLS == nil || CommonNameMatch(r) { 34 | log.Printf("trace: acquire server write lock") 35 | writeMutex.Lock() 36 | inner.ServeHTTP(NewLoggingResponseWriter(w), r) 37 | writeMutex.Unlock() 38 | log.Printf("trace: release server write lock") 39 | } else { 40 | WriteRequestError(NewLoggingResponseWriter(w), http.StatusUnauthorized, 41 | "Authentication Fail with untrusted client cert", []string{}, "") 42 | } 43 | 44 | log.Printf( 45 | "info: request: duration %s", 46 | time.Since(start), 47 | ) 48 | }) 49 | } 50 | 51 | func NewRouter() *mux.Router { 52 | router := mux.NewRouter().StrictSlash(true) 53 | for _, route := range routes { 54 | handler := Middleware(route.HandlerFunc, route.Name) 55 | 56 | router. 57 | Methods(route.Method). 58 | Path(route.Pattern). 59 | Name(route.Name). 60 | Handler(handler) 61 | } 62 | 63 | return router 64 | } 65 | 66 | func Index(w http.ResponseWriter, r *http.Request) { 67 | fmt.Fprintf(w, "Sonic MSEE Restful API v1!") 68 | } 69 | 70 | var routes = Routes{ 71 | Route{ 72 | "Index", 73 | "GET", 74 | "/v1/", 75 | Index, 76 | }, 77 | 78 | Route{ 79 | "StateHeartbeatGet", 80 | "GET", 81 | "/v1/state/heartbeat", 82 | StateHeartbeatGet, 83 | }, 84 | 85 | Route{ 86 | "ConfigResetStatusGet", 87 | "GET", 88 | "/v1/config/resetstatus", 89 | ConfigResetStatusGet, 90 | }, 91 | 92 | Route{ 93 | "ConfigResetStatusPost", 94 | "POST", 95 | "/v1/config/resetstatus", 96 | ConfigResetStatusPost, 97 | }, 98 | 99 | Route{ 100 | "ConfigInterfaceVlanDelete", 101 | "DELETE", 102 | "/v1/config/interface/vlan/{vlan_id}", 103 | ConfigInterfaceVlanDelete, 104 | }, 105 | 106 | Route{ 107 | "ConfigInterfaceVlanGet", 108 | "GET", 109 | "/v1/config/interface/vlan/{vlan_id}", 110 | ConfigInterfaceVlanGet, 111 | }, 112 | 113 | Route{ 114 | "ConfigInterfaceVlanPost", 115 | "POST", 116 | "/v1/config/interface/vlan/{vlan_id}", 117 | ConfigInterfaceVlanPost, 118 | }, 119 | 120 | Route{ 121 | "ConfigInterfaceVlansGet", 122 | "GET", 123 | "/v1/config/interface/vlans", 124 | ConfigInterfaceVlansGet, 125 | }, 126 | 127 | Route{ 128 | "ConfigInterfaceVlansAllGet", 129 | "GET", 130 | "/v1/config/interface/vlans/all", 131 | ConfigInterfaceVlansAllGet, 132 | }, 133 | 134 | Route{ 135 | "ConfigInterfaceVlansMembersAllGet", 136 | "GET", 137 | "/v1/config/interface/vlans/members/all", 138 | ConfigInterfaceVlansMembersAllGet, 139 | }, 140 | 141 | Route{ 142 | "ConfigInterfaceVlanMemberDelete", 143 | "DELETE", 144 | "/v1/config/interface/vlan/{vlan_id}/member/{if_name}", 145 | ConfigInterfaceVlanMemberDelete, 146 | }, 147 | 148 | Route{ 149 | "ConfigInterfaceVlanMemberGet", 150 | "GET", 151 | "/v1/config/interface/vlan/{vlan_id}/member/{if_name}", 152 | ConfigInterfaceVlanMemberGet, 153 | }, 154 | 155 | Route{ 156 | "ConfigInterfaceVlanMemberPost", 157 | "POST", 158 | "/v1/config/interface/vlan/{vlan_id}/member/{if_name}", 159 | ConfigInterfaceVlanMemberPost, 160 | }, 161 | 162 | Route{ 163 | "ConfigInterfaceVlanMembersGet", 164 | "GET", 165 | "/v1/config/interface/vlan/{vlan_id}/members", 166 | ConfigInterfaceVlanMembersGet, 167 | }, 168 | 169 | Route{ 170 | "ConfigInterfaceVlanNeighborDelete", 171 | "DELETE", 172 | "/v1/config/interface/vlan/{vlan_id}/neighbor/{ip_addr}", 173 | ConfigInterfaceVlanNeighborDelete, 174 | }, 175 | 176 | Route{ 177 | "ConfigInterfaceVlanNeighborGet", 178 | "GET", 179 | "/v1/config/interface/vlan/{vlan_id}/neighbor/{ip_addr}", 180 | ConfigInterfaceVlanNeighborGet, 181 | }, 182 | 183 | Route{ 184 | "ConfigInterfaceVlanNeighborPost", 185 | "POST", 186 | "/v1/config/interface/vlan/{vlan_id}/neighbor/{ip_addr}", 187 | ConfigInterfaceVlanNeighborPost, 188 | }, 189 | 190 | Route{ 191 | "ConfigInterfaceVlanNeighborsGet", 192 | "GET", 193 | "/v1/config/interface/vlan/{vlan_id}/neighbors", 194 | ConfigInterfaceVlanNeighborsGet, 195 | }, 196 | 197 | Route{ 198 | "ConfigTunnelDecapTunnelTypeDelete", 199 | "DELETE", 200 | "/v1/config/tunnel/decap/{tunnel_type}", 201 | ConfigTunnelDecapTunnelTypeDelete, 202 | }, 203 | 204 | Route{ 205 | "ConfigTunnelDecapTunnelTypeGet", 206 | "GET", 207 | "/v1/config/tunnel/decap/{tunnel_type}", 208 | ConfigTunnelDecapTunnelTypeGet, 209 | }, 210 | 211 | Route{ 212 | "ConfigTunnelDecapTunnelTypePost", 213 | "POST", 214 | "/v1/config/tunnel/decap/{tunnel_type}", 215 | ConfigTunnelDecapTunnelTypePost, 216 | }, 217 | 218 | Route{ 219 | "ConfigTunnelEncapVxlanVnidDelete", 220 | "DELETE", 221 | "/v1/config/tunnel/encap/vxlan/{vnid}", 222 | ConfigTunnelEncapVxlanVnidDelete, 223 | }, 224 | 225 | Route{ 226 | "ConfigTunnelEncapVxlanVnidGet", 227 | "GET", 228 | "/v1/config/tunnel/encap/vxlan/{vnid}", 229 | ConfigTunnelEncapVxlanVnidGet, 230 | }, 231 | 232 | Route{ 233 | "ConfigTunnelEncapVxlanVnidPost", 234 | "POST", 235 | "/v1/config/tunnel/encap/vxlan/{vnid}", 236 | ConfigTunnelEncapVxlanVnidPost, 237 | }, 238 | 239 | Route{ 240 | "ConfigVrouterVrfIdDelete", 241 | "DELETE", 242 | "/v1/config/vrouter/{vnet_name}", 243 | ConfigVrouterVrfIdDelete, 244 | }, 245 | 246 | Route{ 247 | "ConfigVrouterVrfIdGet", 248 | "GET", 249 | "/v1/config/vrouter/{vnet_name}", 250 | ConfigVrouterVrfIdGet, 251 | }, 252 | 253 | Route{ 254 | "ConfigVrouterVrfIdPost", 255 | "POST", 256 | "/v1/config/vrouter/{vnet_name}", 257 | ConfigVrouterVrfIdPost, 258 | }, 259 | 260 | Route{ 261 | "ConfigVrouterVrfIdRoutesDelete", 262 | "DELETE", 263 | "/v1/config/vrouter/{vnet_name}/routes", 264 | ConfigVrouterVrfIdRoutesDelete, 265 | }, 266 | 267 | Route{ 268 | "ConfigVrouterVrfIdRoutesGet", 269 | "GET", 270 | "/v1/config/vrouter/{vnet_name}/routes", 271 | ConfigVrouterVrfIdRoutesGet, 272 | }, 273 | 274 | Route{ 275 | "ConfigVrouterVrfIdRoutesPatch", 276 | "PATCH", 277 | "/v1/config/vrouter/{vnet_name}/routes", 278 | ConfigVrouterVrfIdRoutesPatch, 279 | }, 280 | 281 | Route{ 282 | "ConfigVrfRouteExpiryGet", 283 | "GET", 284 | "/v1/config/vrf/route_expiry", 285 | ConfigVrfRouteExpiryGet, 286 | }, 287 | 288 | Route{ 289 | "ConfigVrfRouteExpiryPost", 290 | "POST", 291 | "/v1/config/vrf/route_expiry", 292 | ConfigVrfRouteExpiryPost, 293 | }, 294 | 295 | Route{ 296 | "ConfigVrfVrfIdRoutesGet", 297 | "GET", 298 | "/v1/config/vrf/{vrf_id}/routes", 299 | ConfigVrfVrfIdRoutesGet, 300 | }, 301 | 302 | Route{ 303 | "ConfigVrfVrfIdRoutesPatch", 304 | "PATCH", 305 | "/v1/config/vrf/{vrf_id}/routes", 306 | ConfigVrfVrfIdRoutesPatch, 307 | }, 308 | 309 | Route{ 310 | "StateInterfacePortGet", 311 | "GET", 312 | "/v1/state/interface/{port}", 313 | StateInterfacePortGet, 314 | }, 315 | 316 | Route{ 317 | "StateInterfaceGet", 318 | "GET", 319 | "/v1/state/interface", 320 | StateInterfaceGet, 321 | }, 322 | 323 | Route{ 324 | "ConfigBgpProfilePost", 325 | "POST", 326 | "/v1/config/bgp/profile/{profile_name}", 327 | ConfigBgpProfilePost, 328 | }, 329 | 330 | Route{ 331 | "ConfigBgpProfileGet", 332 | "GET", 333 | "/v1/config/bgp/profile/{profile_name}", 334 | ConfigBgpProfileGet, 335 | }, 336 | 337 | Route{ 338 | "ConfigBgpProfileDelete", 339 | "DELETE", 340 | "/v1/config/bgp/profile/{profile_name}", 341 | ConfigBgpProfileDelete, 342 | }, 343 | 344 | // Required to run Unit tests 345 | Route{ 346 | "InMemConfigRestart", 347 | "POST", 348 | "/v1/config/restartdb", 349 | InMemConfigRestart, 350 | }, 351 | 352 | // Adding Ping method from VRF context 353 | Route{ 354 | "Ping", 355 | "POST", 356 | "/v1/operations/ping", 357 | Ping, 358 | }, 359 | 360 | } 361 | -------------------------------------------------------------------------------- /go-server-server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | // WARNING! 5 | // Change this to a fully-qualified import path 6 | // once you place this file into your project. 7 | // For example, 8 | // 9 | // sw "github.com/myname/myrepo/go" 10 | // 11 | "github.com/vharitonsky/iniflags" 12 | sw "go-server-server/go" 13 | "log" 14 | "net/http" 15 | "crypto/tls" 16 | "crypto/x509" 17 | "io/ioutil" 18 | "os" 19 | "os/signal" 20 | "syscall" 21 | "context" 22 | "sync" 23 | "time" 24 | ) 25 | 26 | const CERT_MONITOR_FREQUENCY = 3600 * time.Second 27 | 28 | func StartHttpServer(handler http.Handler) { 29 | log.Printf("info: http endpoint started") 30 | log.Fatal(http.ListenAndServe(":8090", handler)) 31 | } 32 | 33 | func StartHttpsServer(handler http.Handler, messenger <-chan int, wgroup *sync.WaitGroup) { 34 | defer wgroup.Done() 35 | for { 36 | clientCert, err := ioutil.ReadFile(*sw.ClientCertFlag) 37 | if err != nil { 38 | log.Fatalf("error: couldn't open client cert file, %s", err) 39 | } 40 | clientCertPool := x509.NewCertPool() 41 | clientCertPool.AppendCertsFromPEM(clientCert) 42 | 43 | // Setup HTTPS client cert the server trust and validation policy 44 | tlsConfig := &tls.Config{ 45 | ClientCAs: clientCertPool, 46 | // NoClientCert 47 | // RequestClientCert 48 | // RequireAnyClientCert 49 | // VerifyClientCertIfGiven 50 | // RequireAndVerifyClientCert 51 | ClientAuth: tls.RequireAndVerifyClientCert, 52 | MinVersion: tls.VersionTLS12, 53 | } 54 | tlsConfig.BuildNameToCertificate() 55 | 56 | server := &http.Server{ 57 | Addr: ":8081", 58 | Handler: handler, 59 | TLSConfig: tlsConfig, 60 | } 61 | 62 | log.Printf("info: https endpoint started") 63 | 64 | // Listening should happen in a go-routine to prevent blocking 65 | go func() { 66 | if err := server.ListenAndServeTLS(*sw.ServerCertFlag, *sw.ServerKeyFlag); err != nil { 67 | log.Println(err) 68 | } 69 | }() 70 | 71 | value := <-messenger 72 | log.Printf("info: HTTPS Signal received. Shutting down...") 73 | if err := server.Shutdown(context.Background()); err != nil { 74 | log.Printf("trace: HTTPS server Shutdown: %v", err) 75 | } else { 76 | log.Printf("info: HTTPS Server shutdown successful!") 77 | } 78 | switch value { 79 | case 0: 80 | // Signal from signal_handler 81 | log.Printf("info: Terminating...") 82 | if (!*sw.SystemTestFlag) { 83 | os.Exit(0) 84 | } 85 | return 86 | } 87 | } 88 | } 89 | 90 | func signal_handler(messenger chan<- int, wgroup *sync.WaitGroup) { 91 | defer wgroup.Done() 92 | sigchannel := make(chan os.Signal, 1) 93 | signal.Notify(sigchannel, 94 | syscall.SIGTERM, 95 | syscall.SIGQUIT) 96 | 97 | <-sigchannel 98 | messenger <- 0 99 | log.Printf("info: Signal Handler returning...") 100 | return 101 | } 102 | 103 | func monitor_certs(messenger chan<- int, wgroup *sync.WaitGroup) { 104 | defer wgroup.Done() 105 | client_cert_finfo, _ := os.Lstat(*sw.ClientCertFlag) 106 | prev_client_cert_mtime := client_cert_finfo.ModTime() 107 | log.Printf("trace: Last modified time of %s is %d", client_cert_finfo.Name(), prev_client_cert_mtime.Unix()) 108 | 109 | server_cert_finfo, _ := os.Lstat(*sw.ServerCertFlag) 110 | prev_server_cert_mtime := server_cert_finfo.ModTime() 111 | log.Printf("trace: Last modified time of %s is %d", server_cert_finfo.Name(), prev_server_cert_mtime.Unix()) 112 | 113 | sever_key_finfo, _ := os.Lstat(*sw.ServerKeyFlag) 114 | prev_sever_key_mtime := sever_key_finfo.ModTime() 115 | log.Printf("trace: Last modified time of %s is %d", sever_key_finfo.Name(), prev_sever_key_mtime.Unix()) 116 | 117 | time.Sleep(CERT_MONITOR_FREQUENCY) 118 | 119 | for { 120 | reload := false 121 | client_cert_finfo, _ := os.Lstat(*sw.ClientCertFlag) 122 | client_cert_mtime := client_cert_finfo.ModTime() 123 | log.Printf("trace: Last modified time of %s is %d", client_cert_finfo.Name(), client_cert_mtime.Unix()) 124 | if client_cert_mtime != prev_client_cert_mtime { 125 | log.Printf("info: Last modified time of %s changed from %d to %d", client_cert_finfo.Name(), prev_client_cert_mtime.Unix(), client_cert_mtime.Unix()) 126 | reload = true 127 | } 128 | prev_client_cert_mtime = client_cert_mtime 129 | 130 | server_cert_finfo, _ := os.Lstat(*sw.ServerCertFlag) 131 | server_cert_mtime := server_cert_finfo.ModTime() 132 | log.Printf("trace: Last modified time of %s is %d", server_cert_finfo.Name(), server_cert_mtime.Unix()) 133 | if server_cert_mtime != prev_server_cert_mtime { 134 | log.Printf("info: Last modified time of %s changed from %d to %d", server_cert_finfo.Name(), prev_server_cert_mtime.Unix(), server_cert_mtime.Unix()) 135 | reload = true 136 | } 137 | prev_server_cert_mtime = server_cert_mtime 138 | 139 | sever_key_finfo, _ := os.Lstat(*sw.ServerKeyFlag) 140 | sever_key_mtime := sever_key_finfo.ModTime() 141 | log.Printf("trace: Last modified time of %s is %d", sever_key_finfo.Name(), sever_key_mtime.Unix()) 142 | if sever_key_mtime != prev_sever_key_mtime { 143 | log.Printf("info: Last modified time of %s changed from %d to %d", sever_key_finfo.Name(), prev_sever_key_mtime.Unix(), sever_key_mtime.Unix()) 144 | reload = true 145 | } 146 | prev_sever_key_mtime = sever_key_mtime 147 | 148 | if reload == true { 149 | log.Printf("info: Certs have rolled! Reload needed!") 150 | messenger <- 1 151 | } 152 | time.Sleep(CERT_MONITOR_FREQUENCY) 153 | } 154 | } 155 | 156 | func main() { 157 | iniflags.Parse() 158 | 159 | sw.InitLogging() 160 | var wgroup sync.WaitGroup 161 | var messenger = make(chan int, 1) 162 | 163 | log.Printf("info: server started") 164 | 165 | sw.Initialise() 166 | router := sw.NewRouter() 167 | 168 | if (!*sw.HttpFlag && !*sw.HttpsFlag) { 169 | log.Fatal("Both http and http endpoints are disabled.") 170 | } 171 | 172 | if (*sw.HttpFlag) { 173 | go StartHttpServer(router) 174 | } 175 | 176 | if (*sw.HttpsFlag) { 177 | wgroup.Add(1) 178 | go StartHttpsServer(router, messenger, &wgroup) 179 | 180 | if (!*sw.SystemTestFlag) { 181 | wgroup.Add(1) 182 | go monitor_certs(messenger, &wgroup) 183 | } 184 | } 185 | 186 | wgroup.Add(1) 187 | go signal_handler(messenger, &wgroup) 188 | 189 | wgroup.Wait() 190 | } 191 | -------------------------------------------------------------------------------- /go-server-server/main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | "log" 6 | ) 7 | 8 | // Test started when the test binary is started. Only calls main. 9 | func TestSystem(t *testing.T) { 10 | log.Printf("info: Starting Tests...") 11 | main() 12 | log.Printf("info: Ending Tests...") 13 | } 14 | -------------------------------------------------------------------------------- /libcswsscommon/.gitignore: -------------------------------------------------------------------------------- 1 | .depend 2 | *.o 3 | *.a 4 | -------------------------------------------------------------------------------- /libcswsscommon/Makefile: -------------------------------------------------------------------------------- 1 | RM=rm -f 2 | 3 | INCLUDES= \ 4 | -Iinclude \ 5 | -I$(SWSSCOMMON_SRC)/common \ 6 | -I/usr/include/swss 7 | 8 | CPPFLAGS=-g -std=c++11 $(INCLUDES) 9 | LDFLAGS=-g -shared 10 | 11 | SRCS=src/dbconnector.cpp src/producertable.cpp src/producerstatetable.cpp src/table.cpp 12 | OBJS=$(SRCS:.cpp=.o) 13 | %.o: %.cpp 14 | $(CXX) $(CPPFLAGS) -c $< -o $@ 15 | 16 | .PHONY: all swsscommon clean install 17 | 18 | all: swsscommon 19 | 20 | swsscommon: $(OBJS) 21 | $(AR) rvs libcswsscommon.a $(OBJS) 22 | 23 | install: swsscommon 24 | cp libcswsscommon.a /usr/lib # FIXME: better use install 25 | cp -r include/* /usr/include 26 | 27 | clean: 28 | $(RM) $(OBJS) 29 | $(RM) libcswsscommon.a 30 | -------------------------------------------------------------------------------- /libcswsscommon/include/capi/dbconnector.h: -------------------------------------------------------------------------------- 1 | #ifndef _C_DBCONNECTOR_H 2 | #define _C_DBCONNECTOR_H 3 | 4 | #include 5 | #include 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | typedef void *db_connector_t; 12 | 13 | // DBConnector::DBConnector(int db, std::string hostname, int port, unsigned int timeout) 14 | db_connector_t db_connector_new(int db, const char *hostname, int port, unsigned int timeout); 15 | // DBConnector::DBConnector(int db, std::string unixPath, unsigned int timeout) 16 | db_connector_t db_connector_new2(int db, const char *unixPath, unsigned int timeout); 17 | 18 | // DBConnector::~DBConnector() 19 | void db_connector_delete(db_connector_t db); 20 | 21 | // redisContext *DBConnector::getContext() 22 | redisContext *db_connector_get_context(db_connector_t db); 23 | // int DBConnector::getDB() 24 | int db_connector_get_db(db_connector_t db); 25 | 26 | // static void DBConnector::select(DBConnector *db) 27 | void db_connector_select(db_connector_t db); 28 | 29 | // DBConnector *DBConnector::newConnector(unsigned int timeout); 30 | db_connector_t db_connector_new_connector(db_connector_t db, unsigned int timeout); 31 | 32 | #ifdef __cplusplus 33 | } 34 | #endif 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /libcswsscommon/include/capi/producerstatetable.h: -------------------------------------------------------------------------------- 1 | #ifndef _C_PRODUCERSTATETABLE_H 2 | #define _C_PRODUCERSTATETABLE_H 3 | 4 | #include 5 | #include 6 | 7 | #include "producertable.h" 8 | 9 | #ifdef __cplusplus 10 | extern "C" { 11 | #endif 12 | 13 | typedef void *db_connector_t2; 14 | typedef void *redis_pipeline_t; 15 | typedef void *producer_state_table_t; 16 | 17 | // ProducerStateTable::ProducerStateTable(DBConnector *db, std::string tableName) 18 | producer_state_table_t producer_state_table_new(db_connector_t2 db, const char *tableName); 19 | // ProducerStateTable::ProducerStateTable(RedisPipeline *pipeline, std::string tableName, bool buffered = false) 20 | producer_state_table_t producer_state_table_new2(redis_pipeline_t pipeline, const char *tableName, bool buffered); 21 | 22 | // ProducerStateTable::~ProducerStateTable() 23 | void producer_state_table_delete(producer_state_table_t pt); 24 | 25 | // void ProducerStateTable::setBuffered(bool buffered) 26 | void producer_state_table_set_buffered(producer_state_table_t pt, bool buffered); 27 | 28 | // void ProducerStateTable::set(std::string key, 29 | // std::vector &values, 30 | // std::string op = SET_COMMAND, 31 | // std::string prefix = EMPTY_PREFIX) 32 | void producer_state_table_set(producer_state_table_t pt, 33 | const char *key, 34 | const field_value_tuple_t *values, 35 | size_t count, 36 | const char *op, 37 | const char *prefix); 38 | 39 | // void ProducerStateTable::del(std::string key, 40 | // std::string op = DEL_COMMAND, 41 | // std::string prefix = EMPTY_PREFIX) 42 | void producer_state_table_del(producer_state_table_t pt, 43 | const char *key, 44 | const char *op, 45 | const char *prefix); 46 | 47 | // void ProducerStateTable::flush() 48 | void producer_state_table_flush(producer_state_table_t pt); 49 | 50 | #ifdef __cplusplus 51 | } 52 | #endif 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /libcswsscommon/include/capi/producertable.h: -------------------------------------------------------------------------------- 1 | #ifndef _C_PRODUCERTABLE_H 2 | #define _C_PRODUCERTABLE_H 3 | 4 | #include 5 | #include 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | typedef void *db_connector_t; 12 | typedef void *redis_pipeline_t; 13 | typedef void *producer_table_t; 14 | 15 | typedef struct field_value_tuple 16 | { 17 | const char *field; 18 | const char *value; 19 | } field_value_tuple_t; 20 | 21 | // ProducerTable::ProducerTable(DBConnector *db, std::string tableName) 22 | producer_table_t producer_table_new(db_connector_t db, const char *tableName); 23 | // ProducerTable::ProducerTable(RedisPipeline *pipeline, std::string tableName, bool buffered = false) 24 | producer_table_t producer_table_new2(redis_pipeline_t pipeline, const char *tableName, bool buffered); 25 | // ProducerTable::ProducerTable(DBConnector *db, std::string tableName, std::string dumpFile) 26 | producer_table_t producer_table_new3(db_connector_t db, const char *tableName, const char *dumpFile); 27 | 28 | // ProducerTable::~ProducerTable() 29 | void producer_table_delete(producer_table_t pt); 30 | 31 | // void ProducerTable::setBuffered(bool buffered) 32 | void producer_table_set_buffered(producer_table_t pt, bool buffered); 33 | 34 | // void ProducerTable::set(std::string key, 35 | // std::vector &values, 36 | // std::string op = SET_COMMAND, 37 | // std::string prefix = EMPTY_PREFIX) 38 | void producer_table_set(producer_table_t pt, 39 | const char *key, 40 | const field_value_tuple_t *values, 41 | size_t count, 42 | const char *op, 43 | const char *prefix); 44 | 45 | // void ProducerTable::del(std::string key, 46 | // std::string op = DEL_COMMAND, 47 | // std::string prefix = EMPTY_PREFIX) 48 | void producer_table_del(producer_table_t pt, 49 | const char *key, 50 | const char *op, 51 | const char *prefix); 52 | 53 | // void ProducerTable::flush() 54 | void producer_table_flush(producer_table_t pt); 55 | 56 | #ifdef __cplusplus 57 | } 58 | #endif 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /libcswsscommon/include/capi/table.h: -------------------------------------------------------------------------------- 1 | #ifndef _C_TABLE_H 2 | #define _C_TABLE_H 3 | 4 | #include 5 | #include 6 | 7 | #include "producertable.h" 8 | 9 | #ifdef __cplusplus 10 | extern "C" { 11 | #endif 12 | 13 | typedef void *db_connector_t2; 14 | typedef void *redis_pipeline_t; 15 | typedef void *table_t; 16 | 17 | // ProducerStateTable::ProducerStateTable(DBConnector *db, std::string tableName) 18 | table_t table_new(db_connector_t2 db, const char *tableName); 19 | // ProducerStateTable::ProducerStateTable(RedisPipeline *pipeline, std::string tableName, bool buffered = false) 20 | table_t table_new2(redis_pipeline_t pipeline, const char *tableName, bool buffered); 21 | 22 | // ProducerStateTable::~ProducerStateTable() 23 | void table_delete(table_t pt); 24 | 25 | // void ProducerStateTable::setBuffered(bool buffered) 26 | void table_set_buffered(table_t pt, bool buffered); 27 | 28 | // void ProducerStateTable::set(std::string key, 29 | // std::vector &values, 30 | // std::string op = SET_COMMAND, 31 | // std::string prefix = EMPTY_PREFIX) 32 | void table_set(table_t pt, 33 | const char *key, 34 | const field_value_tuple_t *values, 35 | size_t count, 36 | const char *op, 37 | const char *prefix); 38 | 39 | // void ProducerStateTable::del(std::string key, 40 | // std::string op = DEL_COMMAND, 41 | // std::string prefix = EMPTY_PREFIX) 42 | void table_del(table_t pt, 43 | const char *key, 44 | const char *op, 45 | const char *prefix); 46 | 47 | // void ProducerStateTable::flush() 48 | void table_flush(table_t pt); 49 | 50 | #ifdef __cplusplus 51 | } 52 | #endif 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /libcswsscommon/src/dbconnector.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | extern "C" db_connector_t db_connector_new(int db, const char *hostname, int port, unsigned int timeout) 7 | { 8 | auto dbc = new swss::DBConnector(db, std::string(hostname), port, timeout); 9 | return static_cast(dbc); 10 | } 11 | 12 | extern "C" db_connector_t db_connector_new2(int db, const char *unixPath, unsigned int timeout) 13 | { 14 | auto dbc = new swss::DBConnector(db, std::string(unixPath), timeout); 15 | return static_cast(dbc); 16 | } 17 | 18 | extern "C" void db_connector_delete(db_connector_t db) 19 | { 20 | delete static_cast(db); 21 | } 22 | 23 | extern "C" redisContext *db_connector_get_context(db_connector_t db) 24 | { 25 | return static_cast(db)->getContext(); 26 | } 27 | 28 | extern "C" int db_connector_get_db(db_connector_t db) 29 | { 30 | return static_cast(db)->getDbId(); 31 | } 32 | 33 | extern "C" void db_connector_select(db_connector_t db) 34 | { 35 | swss::DBConnector::select(static_cast(db)); 36 | } 37 | 38 | extern "C" db_connector_t db_connector_new_connector(db_connector_t db, unsigned int timeout) 39 | { 40 | auto dbc = static_cast(db)->newConnector(timeout); 41 | return static_cast(dbc); 42 | } 43 | -------------------------------------------------------------------------------- /libcswsscommon/src/producerstatetable.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | producer_state_table_t producer_state_table_new(db_connector_t db, const char *tableName) 11 | { 12 | auto pt = new swss::ProducerStateTable(static_cast(db), std::string(tableName)); 13 | return static_cast(pt); 14 | } 15 | 16 | producer_state_table_t producer_state_table_new2(redis_pipeline_t pipeline, const char *tableName, bool buffered) 17 | { 18 | auto pt = new swss::ProducerStateTable(static_cast(pipeline), std::string(tableName), buffered); 19 | return static_cast(pt); 20 | } 21 | 22 | void producer_state_table_delete(producer_state_table_t pt) 23 | { 24 | delete static_cast(pt); 25 | } 26 | 27 | void producer_state_table_set_buffered(producer_state_table_t pt, bool buffered) 28 | { 29 | static_cast(pt)->setBuffered(buffered); 30 | } 31 | 32 | void producer_state_table_set(producer_state_table_t pt, 33 | const char *key, 34 | const field_value_tuple_t *values, 35 | size_t count, 36 | const char *op, 37 | const char *prefix) 38 | { 39 | std::vector tuples; 40 | for(size_t i = 0; i < count; i++) 41 | { 42 | auto tuple = std::make_pair(std::string(values[i].field), std::string(values[i].value)); 43 | tuples.push_back(tuple); 44 | } 45 | static_cast(pt)->set(std::string(key), tuples, std::string(op), std::string(prefix)); 46 | } 47 | 48 | void producer_state_table_del(producer_state_table_t pt, 49 | const char *key, 50 | const char *op, 51 | const char *prefix) 52 | { 53 | static_cast(pt)->del(std::string(key), std::string(op), std::string(prefix)); 54 | } 55 | 56 | void producer_state_table_flush(producer_state_table_t pt) 57 | { 58 | static_cast(pt)->flush(); 59 | } 60 | -------------------------------------------------------------------------------- /libcswsscommon/src/producertable.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | producer_table_t producer_table_new(db_connector_t db, const char *tableName) 11 | { 12 | auto pt = new swss::ProducerTable(static_cast(db), std::string(tableName)); 13 | return static_cast(pt); 14 | } 15 | 16 | producer_table_t producer_table_new2(redis_pipeline_t pipeline, const char *tableName, bool buffered) 17 | { 18 | auto pt = new swss::ProducerTable(static_cast(pipeline), std::string(tableName), buffered); 19 | return static_cast(pt); 20 | } 21 | 22 | producer_table_t producer_table_new3(db_connector_t db, const char *tableName, const char *dumpFile) 23 | { 24 | auto pt = new swss::ProducerTable(static_cast(db), std::string(tableName), std::string(dumpFile)); 25 | return static_cast(pt); 26 | } 27 | 28 | void producer_table_delete(producer_table_t pt) 29 | { 30 | delete static_cast(pt); 31 | } 32 | 33 | void producer_table_set_buffered(producer_table_t pt, bool buffered) 34 | { 35 | static_cast(pt)->setBuffered(buffered); 36 | } 37 | 38 | void producer_table_set(producer_table_t pt, 39 | const char *key, 40 | const field_value_tuple_t *values, 41 | size_t count, 42 | const char *op, 43 | const char *prefix) 44 | { 45 | std::vector tuples; 46 | for(size_t i = 0; i < count; i++) 47 | { 48 | auto tuple = std::make_pair(std::string(values[i].field), std::string(values[i].value)); 49 | tuples.push_back(tuple); 50 | } 51 | static_cast(pt)->set(std::string(key), tuples, std::string(op), std::string(prefix)); 52 | } 53 | 54 | void producer_table_del(producer_table_t pt, 55 | const char *key, 56 | const char *op, 57 | const char *prefix) 58 | { 59 | static_cast(pt)->del(std::string(key), std::string(op), std::string(prefix)); 60 | } 61 | 62 | void producer_table_flush(producer_table_t pt) 63 | { 64 | static_cast(pt)->flush(); 65 | } 66 | -------------------------------------------------------------------------------- /libcswsscommon/src/table.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | table_t table_new(db_connector_t db, const char *tableName) 11 | { 12 | auto pt = new swss::Table(static_cast(db), std::string(tableName)); 13 | return static_cast(pt); 14 | } 15 | 16 | table_t table_new2(redis_pipeline_t pipeline, const char *tableName, bool buffered) 17 | { 18 | auto pt = new swss::Table(static_cast(pipeline), std::string(tableName), buffered); 19 | return static_cast(pt); 20 | } 21 | 22 | void table_delete(table_t pt) 23 | { 24 | delete static_cast(pt); 25 | } 26 | 27 | void table_set_buffered(table_t pt, bool buffered) 28 | { 29 | static_cast(pt)->setBuffered(buffered); 30 | } 31 | 32 | void table_set(table_t pt, 33 | const char *key, 34 | const field_value_tuple_t *values, 35 | size_t count, 36 | const char *op, 37 | const char *prefix) 38 | { 39 | std::vector tuples; 40 | for(size_t i = 0; i < count; i++) 41 | { 42 | auto tuple = std::make_pair(std::string(values[i].field), std::string(values[i].value)); 43 | tuples.push_back(tuple); 44 | } 45 | static_cast(pt)->set(std::string(key), tuples, std::string(op), std::string(prefix)); 46 | } 47 | 48 | void table_del(table_t pt, 49 | const char *key, 50 | const char *op, 51 | const char *prefix) 52 | { 53 | static_cast(pt)->del(std::string(key), std::string(op), std::string(prefix)); 54 | } 55 | 56 | void table_flush(table_t pt) 57 | { 58 | static_cast(pt)->flush(); 59 | } 60 | -------------------------------------------------------------------------------- /mseethrift/GoUnusedProtection__.go: -------------------------------------------------------------------------------- 1 | // Autogenerated by Thrift Compiler (0.10.0) 2 | // DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 3 | 4 | package msee 5 | 6 | var GoUnusedProtection__ int; 7 | 8 | -------------------------------------------------------------------------------- /mseethrift/msee-consts.go: -------------------------------------------------------------------------------- 1 | // Autogenerated by Thrift Compiler (0.10.0) 2 | // DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 3 | 4 | package msee 5 | 6 | import ( 7 | "bytes" 8 | "fmt" 9 | "git.apache.org/thrift.git/lib/go/thrift" 10 | ) 11 | 12 | // (needed to ensure safety because of naive import list construction.) 13 | var _ = thrift.ZERO 14 | var _ = fmt.Printf 15 | var _ = bytes.Equal 16 | 17 | 18 | func init() { 19 | } 20 | 21 | -------------------------------------------------------------------------------- /mseethrifttest/Makefile: -------------------------------------------------------------------------------- 1 | #FIXME: Create better rules 2 | #FIXME: Check and write all dependencies 3 | 4 | RACE_OPTION := -race 5 | ifeq ($(CONFIGURED_ARCH),armhf) 6 | RACE_OPTION := 7 | endif 8 | 9 | .INTERMEDIATE: msee.thrift.intermediate 10 | 11 | all: client 12 | 13 | client: client.go ../mseethrift/msee.go 14 | go build $(RACE_OPTION) client.go 15 | 16 | msee/constants.go msee/msee.go msee/ttypes.go: msee.thrift.intermediate 17 | go get "git.apache.org/thrift.git/lib/go/thrift" 18 | 19 | msee.thrift.intermediate: msee.thrift 20 | thrift -out . --gen go $< 21 | 22 | clean: 23 | rm -fr msee 24 | rm -f client 25 | -------------------------------------------------------------------------------- /mseethrifttest/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "mseethrift" 6 | "git.apache.org/thrift.git/lib/go/thrift" 7 | ) 8 | 9 | func main() { 10 | transport, err := thrift.NewTSocket("localhost:9090") 11 | 12 | if err != nil { 13 | fmt.Println("Error opening socket:", err) 14 | return 15 | } 16 | 17 | defer transport.Close() 18 | if err := transport.Open(); err != nil { 19 | fmt.Println("Error opening transport", err) 20 | return 21 | } 22 | 23 | protocol := thrift.NewTBinaryProtocolTransport(transport) 24 | 25 | client := msee.NewMSEEClientProtocol(transport, protocol, protocol) 26 | 27 | result, err := client.SetSwitchAddr(0x4c7625f52a80, 0x194e9865) 28 | checkResult("SetSwitchAddr", result, err) 29 | 30 | result, err = client.AddPortToVrf("Ethernet0", "1234") 31 | checkResult("AddPortToVrf", result, err) 32 | 33 | result, err = client.AddVroute("1234", 0xc0a80204, 32, 17291, 0x0a508818) 34 | checkResult("AddVroute", result, err) 35 | 36 | result, err = client.AddVroute("1234", 0xc0a80206, 32, 17291, 0x0a508c17) 37 | checkResult("AddVroute", result, err) 38 | 39 | result, err = client.AddVroute("1234", 0xc0a80205, 32, 17291, 0x0a508819) 40 | checkResult("AddVroute", result, err) 41 | } 42 | 43 | func checkResult(method string, result bool, err error) { 44 | if err != nil { 45 | fmt.Printf("Error %s\n", method) 46 | } else { 47 | fmt.Printf("Result of %s: %v\n", method, result) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /mseethrifttest/msee.thrift: -------------------------------------------------------------------------------- 1 | typedef i32 msee_ip4_t 2 | typedef i64 msee_mac_t 3 | typedef i16 msee_vlan_t 4 | typedef i64 msee_object_id_t 5 | typedef i32 msee_vni_t 6 | typedef i32 msee_vrf_id_t 7 | typedef i16 msee_udp_port_t 8 | typedef byte msee_port_t 9 | typedef byte msee_prefix_len_t 10 | typedef byte msee_port_count_t 11 | 12 | struct msee_ip6_t 13 | { 14 | 1: i64 low; 15 | 2: i64 high; 16 | } 17 | 18 | enum ip_type_t 19 | { 20 | v4 = 4, 21 | v6 = 6 22 | } 23 | 24 | union msee_ip_t 25 | { 26 | 1: msee_ip4_t ip4; 27 | 2: msee_ip6_t ip6; 28 | } 29 | 30 | struct msee_ip_address_t 31 | { 32 | 1: msee_ip_t ip; 33 | 2: ip_type_t type; 34 | } 35 | 36 | struct msee_ip_prefix_t 37 | { 38 | 1: msee_ip_address_t ip; 39 | 2: msee_prefix_len_t mask_length; 40 | } 41 | 42 | typedef string msee_group_t 43 | typedef byte msee_queue_id_t 44 | typedef string msee_counter_name 45 | typedef string msee_statistics_name 46 | typedef map> counters_t; 47 | typedef map> statistics_t; 48 | typedef map> hist_t; 49 | 50 | enum result_t 51 | { 52 | OK, 53 | ERROR, 54 | ADDED, 55 | UPDATED, 56 | REMOVED, 57 | INVALID_PARAMETERS, 58 | NO_MEMORY, 59 | NOT_FOUND, 60 | ALREADY_EXISTS 61 | } 62 | 63 | service MSEE 64 | { 65 | result_t init_dpdk_port(1: msee_port_count_t nb_customer_ports, 2: msee_mac_t mac_addr, 3: msee_ip4_t ipv4_loaddr, 4: msee_ip6_t ipv6_loaddr); 66 | 67 | // In MSEE scenario stag, ctag and port must all be set to correct values 68 | // In Baremetal, stag and ctag should be set to 0, port must be correct 69 | 70 | result_t add_port_to_vrf(1: msee_vrf_id_t vrf_id, 2: msee_port_t port, 3: msee_vlan_t outer_vlan, 4: msee_vlan_t inner_vlan); 71 | result_t delete_port_from_vrf(1: msee_port_t port, 2: msee_vlan_t outer_vlan, 3: msee_vlan_t inner_vlan); 72 | 73 | result_t map_vni_to_vrf(1: msee_vni_t vni, 2: msee_vrf_id_t vrf_id); 74 | result_t unmap_vni_to_vrf(1: msee_vni_t vni); 75 | 76 | result_t add_encap_route(1: msee_vrf_id_t vrf_id, 2: msee_ip_prefix_t dst_vm_ip_prefix, 3: msee_ip_address_t dst_host_ip, 4: msee_mac_t dst_mac_address, 5: msee_vni_t vni, 6: msee_udp_port_t port); 77 | result_t delete_encap_route(1: msee_vrf_id_t vrf_id, 2: msee_ip_prefix_t dst_vm_ip_prefix); 78 | 79 | result_t add_decap_route(1: msee_vrf_id_t vrf_id, 2: msee_ip_prefix_t dst_ip_prefix, 3: msee_mac_t mac, 4: msee_port_t port, 5: msee_vlan_t outer_vlan, 6: msee_vlan_t inner_vlan); 80 | result_t delete_decap_route(1: msee_vrf_id_t vrf_id, 2: msee_ip_prefix_t dst_ip_prefix); 81 | 82 | // group parameter could be "dpdk.total", "dpdk.switch_ports", "dpdk.nic", or "" to output everything 83 | counters_t get_counters(1: msee_group_t group); 84 | 85 | // group parameter could be "rings", "mempools", "fibs", or "" to output everything 86 | statistics_t get_statistics(1: msee_group_t group); 87 | hist_t get_hist(); 88 | } 89 | 90 | // list of return values for every call which return result_t 91 | // 92 | // init_dpdk_port: OK, INVALID_PARAMETERS 93 | // add_port_to_vrf: INVALID_PARAMETERS, ADDED, ALREADY_EXISTS, NO_MEMORY 94 | // delete_port_from_vrf: INVALID_PARAMETERS, REMOVED, NOT_FOUND 95 | // map_vni_to_vrf: INVALID_PARAMETERS, ADDED, ALREADY_EXISTS, NO_MEMORY 96 | // unmap_vni_to_vrf: INVALID_PARAMETERS, REMOVED, NOT_FOUND 97 | // add_encap_route: INVALID_PARAMETERS, ADDED, UPDATED, NO_MEMORY 98 | // delete_encap_route: INVALID_PARAMETERS, REMOVED, NOT_FOUND 99 | // add_decap_route: INVALID_PARAMETERS, ADDED, UPDATED, NO_MEMORY 100 | // delete_decap_route: INVALID_PARAMETERS, REMOVED, NOT_FOUND -------------------------------------------------------------------------------- /mseethrifttest/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "mseethrift" 6 | "git.apache.org/thrift.git/lib/go/thrift" 7 | ) 8 | 9 | func main() { 10 | transport, err := thrift.NewTServerSocket("localhost:9090") 11 | 12 | if err != nil { 13 | fmt.Println("Error opening socket:", err) 14 | return 15 | } 16 | 17 | defer transport.Close() 18 | if err := transport.Open(); err != nil { 19 | fmt.Println("Error opening transport", err) 20 | return 21 | } 22 | 23 | handler := NewMSEEHandler() 24 | processor := msee.NewMSEEProcessor(handler) 25 | server := thrift.NewTSimpleServer2(processor, transport) 26 | 27 | fmt.Println("Starting server...") 28 | server.Serve() 29 | } 30 | 31 | type MSEEHandler struct { 32 | } 33 | 34 | func NewMSEEHandler() *MSEEHandler { 35 | return &MSEEHandler{} 36 | } 37 | 38 | func (p *MSEEHandler) InitDpdkPort(nb_customer_ports msee.MseePortCountT, mac_addr msee.MseeMacT, ipv4_loaddr msee.MseeIp4T, ipv6_loaddr *msee.MseeIp6T) (r msee.ResultT, err error) { 39 | fmt.Printf("init_dpdk_port(%v, %v, %+v, %+v)\n", nb_customer_ports, mac_addr, ipv4_loaddr, ipv6_loaddr) 40 | r = msee.ResultT_OK 41 | return 42 | } 43 | 44 | func (p *MSEEHandler) AddPortToVrf(vrf_id msee.MseeVrfIDT, port msee.MseePortT, outer_vlan msee.MseeVlanT, inner_vlan msee.MseeVlanT) (r msee.ResultT, err error) { 45 | fmt.Printf("add_port_to_vrf(%v, %v, %v, %v)\n", vrf_id, port, outer_vlan, inner_vlan) 46 | r = msee.ResultT_ADDED 47 | return 48 | } 49 | 50 | func (p *MSEEHandler) DeletePortFromVrf(port msee.MseePortT, outer_vlan msee.MseeVlanT, inner_vlan msee.MseeVlanT) (r msee.ResultT, err error) { 51 | fmt.Printf("delete_port_from_vrf(%v, %v, %v)\n", port, outer_vlan, inner_vlan) 52 | r = msee.ResultT_REMOVED 53 | return 54 | } 55 | 56 | func (p *MSEEHandler)MapVniToVrf(vni msee.MseeVniT, vrf_id msee.MseeVrfIDT) (r msee.ResultT, err error) { 57 | fmt.Printf("map_vni_to_vrf(%v, %v)\n", vni, vrf_id) 58 | r = msee.ResultT_ADDED 59 | return 60 | } 61 | 62 | func (p *MSEEHandler) UnmapVniToVrf(vni msee.MseeVniT) (r msee.ResultT, err error) { 63 | fmt.Printf("unmap_vni_to_vrf(%v)\n", vni) 64 | r = msee.ResultT_REMOVED 65 | return 66 | } 67 | 68 | func (p *MSEEHandler) AddEncapRoute(vrf_id msee.MseeVrfIDT, dst_vm_ip_prefix *msee.MseeIPPrefixT, dst_host_ip *msee.MseeIPAddressT, dst_mac_address msee.MseeMacT, vni msee.MseeVniT, port msee.MseeUDPPortT) (r msee.ResultT, err error) { 69 | fmt.Printf("add_encap_route(%v, %v, %v, %v, %v, %v)\n", vrf_id, dst_vm_ip_prefix, dst_host_ip, dst_mac_address, vni, uint16(port)) 70 | r = msee.ResultT_ADDED 71 | return 72 | } 73 | 74 | func (p *MSEEHandler) DeleteEncapRoute(vrf_id msee.MseeVrfIDT, dst_vm_ip_prefix *msee.MseeIPPrefixT) (r msee.ResultT, err error) { 75 | fmt.Printf("delete_encap_route(%v, %v)\n", vrf_id, dst_vm_ip_prefix) 76 | r = msee.ResultT_REMOVED 77 | return 78 | } 79 | 80 | func (p *MSEEHandler) AddDecapRoute(vrf_id msee.MseeVrfIDT, dst_ip_prefix *msee.MseeIPPrefixT, mac msee.MseeMacT, port msee.MseePortT, outer_vlan msee.MseeVlanT, inner_vlan msee.MseeVlanT) (r msee.ResultT, err error) { 81 | fmt.Printf("add_decap_route(%v, %v, %v, %v, %v, %v)\n", vrf_id, dst_ip_prefix, mac, port, outer_vlan, inner_vlan) 82 | r = msee.ResultT_ADDED 83 | return 84 | } 85 | 86 | func (p *MSEEHandler) DeleteDecapRoute(vrf_id msee.MseeVrfIDT, dst_ip_prefix *msee.MseeIPPrefixT) (r msee.ResultT, err error) { 87 | fmt.Printf("delete_decap_route(%v, %v)\n", vrf_id, dst_ip_prefix) 88 | r = msee.ResultT_REMOVED 89 | return 90 | } 91 | 92 | func (p *MSEEHandler) GetCounters(group msee.MseeGroupT) (r msee.CountersT, err error) { 93 | fmt.Printf("get_counters(%v)\n", group) 94 | if len(group) > 0 { 95 | r = msee.CountersT{group: map[msee.MseeCounterName]int64{"0.decap_ok": 1, "0.encap_lpm_not_found": 2}} 96 | } else { 97 | r = msee.CountersT{"dpdk.switch_ports": map[msee.MseeCounterName]int64{"0.decap_ok": 1, "0.encap_lpm_not_found": 2, "100.encap_lpm_not_found": 2, "foo": 2}, "dpdk.total": map[msee.MseeCounterName]int64{"0.foo": 3, "bar": 4}} 98 | } 99 | return 100 | } 101 | 102 | func (p *MSEEHandler) GetStatistics(group msee.MseeGroupT) (r msee.StatisticsT, err error) { 103 | fmt.Printf("get_statistics(%v)\n", group) 104 | if len(group) > 0 { 105 | r = msee.StatisticsT{group: map[msee.MseeStatisticsName]int64{"foo": 1, "bar": 2}} 106 | } else { 107 | r = msee.StatisticsT{"rings": map[msee.MseeStatisticsName]int64{"foo": 1, "bar": 2}, "mempools": map[msee.MseeStatisticsName]int64{"foo": 3, "bar": 4}} 108 | } 109 | return 110 | } 111 | 112 | func (p *MSEEHandler) GetHist() (r msee.HistT, err error) { 113 | fmt.Printf("get_hist()\n") 114 | r = msee.HistT{0: map[int8]float64{1: 1.1, 2: 2.1}, 1: map[int8]float64{1: 1.1, 2: 2.1}} 115 | return 116 | } -------------------------------------------------------------------------------- /start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | supervisorctl start redis-server 4 | supervisorctl start rest-api 5 | -------------------------------------------------------------------------------- /supervisor/arp_mock_server.conf: -------------------------------------------------------------------------------- 1 | [program:arp_mock_server] 2 | command=/usr/sbin/arp_mock_server 3 | priority=3 4 | autostart=true 5 | autorestart=false 6 | stdout_logfile=/tmp/arp_mock_server.out.log 7 | stderr_logfile=/tmp/arp_mock_server.err.log 8 | 9 | -------------------------------------------------------------------------------- /supervisor/arp_responder_bm.conf: -------------------------------------------------------------------------------- 1 | [program:arp_responder] 2 | command=/usr/sbin/arpresponder_bm 3 | priority=3 4 | autostart=true 5 | autorestart=false 6 | stdout_logfile=/tmp/arp_responder.out.log 7 | stderr_logfile=/tmp/arp_responder.err.log 8 | 9 | -------------------------------------------------------------------------------- /supervisor/arp_responder_msee.conf: -------------------------------------------------------------------------------- 1 | [program:arp_responder] 2 | command=/usr/sbin/arpresponder_msee 3 | priority=3 4 | autostart=true 5 | autorestart=false 6 | stdout_logfile=/tmp/arp_responder.out.log 7 | stderr_logfile=/tmp/arp_responder.err.log 8 | 9 | -------------------------------------------------------------------------------- /supervisor/redis.conf: -------------------------------------------------------------------------------- 1 | [program:redis-server] 2 | command=/usr/bin/redis-server /etc/redis/redis.conf 3 | priority=1 4 | autostart=true 5 | autorestart=false 6 | stdout_logfile=/tmp/redis-out.log 7 | stderr_logfile=/tmp/redis-err.log 8 | -------------------------------------------------------------------------------- /supervisor/rest_api.conf: -------------------------------------------------------------------------------- 1 | [program:rest-api] 2 | command=/usr/sbin/go-server-server -enablehttps=true -clientcert=/usr/sbin/cert/client/selfsigned.crt -servercert=/usr/sbin/cert/server/selfsigned.crt -serverkey=/usr/sbin/cert/server/selfsigned.key -loglevel trace 3 | priority=1 4 | autostart=true 5 | autorestart=false 6 | stdout_logfile=/tmp/rest-api.out.log 7 | stderr_logfile=/tmp/rest-api.err.log 8 | -------------------------------------------------------------------------------- /supervisor/rest_api_test.conf: -------------------------------------------------------------------------------- 1 | [program:rest-api] 2 | command=/usr/sbin/go-server-server.test -test.coverprofile=/coverage.cov -systemtest=true -enablehttps=true -clientcert=/usr/sbin/cert/client/selfsigned.crt -servercert=/usr/sbin/cert/server/selfsigned.crt -serverkey=/usr/sbin/cert/server/selfsigned.key -localapitestdocker=true -loglevel trace 3 | priority=1 4 | autostart=false 5 | autorestart=false 6 | stdout_logfile=/tmp/rest-api.out.log 7 | stderr_logfile=/tmp/rest-api.err.log 8 | 9 | [program:start.sh] 10 | command=/usr/bin/start.sh 11 | priority=1 12 | autostart=true 13 | autorestart=false 14 | stdout_logfile=/tmp/start.out.log 15 | stderr_logfile=/tmp/start.err.log 16 | 17 | [program:CreateMockPort.sh] 18 | command=/usr/bin/CreateMockPort.sh 19 | priority=1 20 | autostart=true 21 | autorestart=false 22 | stdout_logfile=/tmp/CreateMockPort.out.log 23 | stderr_logfile=/tmp/CreateMockPort.err.log 24 | 25 | [program:ServerKill] 26 | command=/usr/bin/server_kill.py 27 | priority=1 28 | autostart=true 29 | autorestart=false 30 | -------------------------------------------------------------------------------- /supervisor/supervisor.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | logfile_maxbytes=1MB 3 | logfile_backups=2 4 | nodaemon=true 5 | -------------------------------------------------------------------------------- /supervisor/thrift_mock_server.conf: -------------------------------------------------------------------------------- 1 | [program:thrift_mock_server] 2 | command=/usr/sbin/thrift_mock_server 3 | priority=3 4 | autostart=true 5 | autorestart=false 6 | stdout_logfile=/tmp/thrift_mock_server.out.log 7 | stderr_logfile=/tmp/thrift_mock_server.err.log 8 | 9 | -------------------------------------------------------------------------------- /swsscommon/dbconnector.go: -------------------------------------------------------------------------------- 1 | package swsscommon 2 | 3 | // #cgo LDFLAGS: -lcswsscommon -lswsscommon -lstdc++ 4 | // #include 5 | // #include 6 | import "C" 7 | 8 | import ( 9 | "unsafe" 10 | ) 11 | 12 | type DBConnector struct { 13 | ptr unsafe.Pointer 14 | } 15 | 16 | func NewDBConnector(db int, hostname string, port int, timeout uint) DBConnector { 17 | hostnameC := C.CString(hostname) 18 | defer C.free(unsafe.Pointer(hostnameC)) 19 | dbc := C.db_connector_new(C.int(db), hostnameC, C.int(port), C.uint(timeout)) 20 | return DBConnector{ptr: unsafe.Pointer(dbc)} 21 | } 22 | 23 | func NewDBConnector2(db int, unixPath string, timeout uint) DBConnector { 24 | unixPathC := C.CString(unixPath) 25 | defer C.free(unsafe.Pointer(unixPathC)) 26 | dbc := C.db_connector_new2(C.int(db), unixPathC, C.uint(timeout)) 27 | return DBConnector{ptr: unsafe.Pointer(dbc)} 28 | } 29 | 30 | func (db DBConnector) Delete() { 31 | C.db_connector_delete(C.db_connector_t(db.ptr)) 32 | } 33 | 34 | func (db DBConnector) GetDB() int { 35 | return int(C.db_connector_get_db(C.db_connector_t(db.ptr))) 36 | } 37 | 38 | func DBConnectorSelect(db DBConnector) { 39 | C.db_connector_select(C.db_connector_t(db.ptr)) 40 | } 41 | 42 | func (db DBConnector) NewConnector(timeout uint) DBConnector { 43 | dbc := C.db_connector_new_connector(C.db_connector_t(db.ptr), C.uint(timeout)) 44 | return DBConnector{ptr: unsafe.Pointer(dbc)}; 45 | } 46 | -------------------------------------------------------------------------------- /swsscommon/go.mod: -------------------------------------------------------------------------------- 1 | module swsscommon 2 | 3 | go 1.15 4 | -------------------------------------------------------------------------------- /swsscommon/producerstatetable.go: -------------------------------------------------------------------------------- 1 | package swsscommon 2 | 3 | // #cgo LDFLAGS: -lcswsscommon -lswsscommon -lstdc++ 4 | // #include 5 | // #include 6 | import "C" 7 | 8 | import ( 9 | "log" 10 | "unsafe" 11 | ) 12 | 13 | type ProducerStateTable struct { 14 | ptr unsafe.Pointer 15 | table string 16 | } 17 | 18 | 19 | func NewProducerStateTable(db DBConnector, tableName string) ProducerStateTable { 20 | tableNameC := C.CString(tableName) 21 | defer C.free(unsafe.Pointer(tableNameC)) 22 | 23 | pt := C.producer_state_table_new(C.db_connector_t2(db.ptr), tableNameC) 24 | return ProducerStateTable{ptr: unsafe.Pointer(pt), table: tableName} 25 | } 26 | 27 | func (pt ProducerStateTable) Delete() { 28 | C.producer_state_table_delete(C.producer_state_table_t(pt.ptr)) 29 | } 30 | 31 | func (pt ProducerStateTable) SetBuffered(buffered bool) { 32 | C.producer_state_table_set_buffered(C.producer_state_table_t(pt.ptr), C._Bool(buffered)) 33 | } 34 | 35 | func (pt ProducerStateTable) Set(key string, values map[string]string, op string, prefix string) { 36 | log.Printf( 37 | "trace: swss: %s %s:%s %s", 38 | op, 39 | pt.table, 40 | key, 41 | values, 42 | ) 43 | 44 | keyC := C.CString(key) 45 | defer C.free(unsafe.Pointer(keyC)) 46 | opC := C.CString(op) 47 | defer C.free(unsafe.Pointer(opC)) 48 | prefixC := C.CString(prefix) 49 | defer C.free(unsafe.Pointer(prefixC)) 50 | 51 | count := len(values) 52 | tuplePtr := (*C.field_value_tuple_t)(C.malloc(C.size_t(C.sizeof_field_value_tuple_t * count))) 53 | defer C.free(unsafe.Pointer(tuplePtr)) 54 | // Get a Go slice to the C array - this doesn't allocate anything 55 | tuples := (*[(1 << 28) - 1]C.field_value_tuple_t)(unsafe.Pointer(tuplePtr))[:count:count] 56 | 57 | idx := 0 58 | for k, v := range values { 59 | kC := C.CString(k) 60 | defer C.free(unsafe.Pointer(kC)) 61 | vC := C.CString(v) 62 | defer C.free(unsafe.Pointer(vC)) 63 | tuples[idx] = C.field_value_tuple_t{ 64 | field: (*C.char)(kC), 65 | value: (*C.char)(vC), 66 | } 67 | idx = idx + 1 68 | } 69 | 70 | C.producer_state_table_set(C.producer_state_table_t(pt.ptr), keyC, tuplePtr, C.size_t(count), opC, prefixC) 71 | } 72 | 73 | func (pt ProducerStateTable) Del(key string, op string, prefix string) { 74 | log.Printf( 75 | "trace: swss: %s %s:%s", 76 | op, 77 | pt.table, 78 | key, 79 | ) 80 | 81 | keyC := C.CString(key) 82 | defer C.free(unsafe.Pointer(keyC)) 83 | opC := C.CString(op) 84 | defer C.free(unsafe.Pointer(opC)) 85 | prefixC := C.CString(prefix) 86 | defer C.free(unsafe.Pointer(prefixC)) 87 | 88 | C.producer_state_table_del(C.producer_state_table_t(pt.ptr), keyC, opC, prefixC) 89 | } 90 | 91 | func (pt ProducerStateTable) Flush() { 92 | C.producer_state_table_flush(C.producer_state_table_t(pt.ptr)) 93 | } 94 | -------------------------------------------------------------------------------- /swsscommon/table.go: -------------------------------------------------------------------------------- 1 | package swsscommon 2 | 3 | // #cgo LDFLAGS: -lcswsscommon -lswsscommon -lstdc++ 4 | // #include 5 | // #include 6 | import "C" 7 | 8 | import ( 9 | "log" 10 | "unsafe" 11 | ) 12 | 13 | type Table struct { 14 | ptr unsafe.Pointer 15 | table string 16 | } 17 | 18 | 19 | func NewTable(db DBConnector, tableName string) Table { 20 | tableNameC := C.CString(tableName) 21 | defer C.free(unsafe.Pointer(tableNameC)) 22 | 23 | pt := C.table_new(C.db_connector_t2(db.ptr), tableNameC) 24 | return Table{ptr: unsafe.Pointer(pt), table: tableName} 25 | } 26 | 27 | func (pt Table) Delete() { 28 | C.table_delete(C.table_t(pt.ptr)) 29 | } 30 | 31 | func (pt Table) SetBuffered(buffered bool) { 32 | C.table_set_buffered(C.table_t(pt.ptr), C._Bool(buffered)) 33 | } 34 | 35 | func (pt Table) Set(key string, values map[string]string, op string, prefix string) { 36 | log.Printf( 37 | "trace: swss: %s %s:%s %s", 38 | op, 39 | pt.table, 40 | key, 41 | values, 42 | ) 43 | 44 | keyC := C.CString(key) 45 | defer C.free(unsafe.Pointer(keyC)) 46 | opC := C.CString(op) 47 | defer C.free(unsafe.Pointer(opC)) 48 | prefixC := C.CString(prefix) 49 | defer C.free(unsafe.Pointer(prefixC)) 50 | 51 | count := len(values) 52 | tuplePtr := (*C.field_value_tuple_t)(C.malloc(C.size_t(C.sizeof_field_value_tuple_t * count))) 53 | defer C.free(unsafe.Pointer(tuplePtr)) 54 | // Get a Go slice to the C array - this doesn't allocate anything 55 | tuples := (*[(1 << 28) - 1]C.field_value_tuple_t)(unsafe.Pointer(tuplePtr))[:count:count] 56 | 57 | idx := 0 58 | for k, v := range values { 59 | kC := C.CString(k) 60 | defer C.free(unsafe.Pointer(kC)) 61 | vC := C.CString(v) 62 | defer C.free(unsafe.Pointer(vC)) 63 | tuples[idx] = C.field_value_tuple_t{ 64 | field: (*C.char)(kC), 65 | value: (*C.char)(vC), 66 | } 67 | idx = idx + 1 68 | } 69 | 70 | C.table_set(C.table_t(pt.ptr), keyC, tuplePtr, C.size_t(count), opC, prefixC) 71 | } 72 | 73 | func (pt Table) Del(key string, op string, prefix string) { 74 | log.Printf( 75 | "trace: swss: %s %s:%s", 76 | op, 77 | pt.table, 78 | key, 79 | ) 80 | 81 | keyC := C.CString(key) 82 | defer C.free(unsafe.Pointer(keyC)) 83 | opC := C.CString(op) 84 | defer C.free(unsafe.Pointer(opC)) 85 | prefixC := C.CString(prefix) 86 | defer C.free(unsafe.Pointer(prefixC)) 87 | 88 | C.table_del(C.table_t(pt.ptr), keyC, opC, prefixC) 89 | } 90 | 91 | func (pt Table) Flush() { 92 | C.table_flush(C.table_t(pt.ptr)) 93 | } 94 | -------------------------------------------------------------------------------- /swsscommontest/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "swsscommon" 4 | 5 | func main() { 6 | db := swsscommon.NewDBConnector(0, "localhost", 6379, 0); 7 | defer db.Delete() 8 | pt := swsscommon.NewProducerStateTable(db, "QOS_TABLE") 9 | defer pt.Delete() 10 | pt.Del("PORT_TABLE:ETHERNET4", "DEL", "") 11 | pt.Set("SCHEDULER_TABLE:SCAVENGER", map[string]string{ 12 | "algorithm": "DWRR", 13 | "weight": "35", 14 | }, "SET", "") 15 | } 16 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | 2 | # Start rest api container 3 | docker run -d --rm -p8080:8080 -p6379:6379 --name rest-api --cap-add NET_ADMIN --privileged -t rest-api-image:latest 4 | 5 | # Wait until everything started 6 | sleep 5 7 | 8 | # Run out tests 9 | cd test 10 | python apitest.py 11 | 12 | # Stop container 13 | docker stop rest-api 14 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | test.log 2 | -------------------------------------------------------------------------------- /test/config/arpresponder.conf: -------------------------------------------------------------------------------- 1 | Ethernet12 2 | Ethernet16 3 | -------------------------------------------------------------------------------- /test/config/config.ini: -------------------------------------------------------------------------------- 1 | loaddr4=127.0.0.1 2 | loaddr6=::0 3 | swmacaddr=01:02:03:04:05:06 4 | dpdkmacaddr=01:02:03:04:05:06 5 | dpdkport=Ethernet0 6 | pcports=Ethernet4,Ethernet8 7 | loglevel=trace 8 | ports=Ethernet12,Ethernet16,Ethernet34 9 | enablehttp=true 10 | enablehttps=true 11 | clientcert=../cert/client/selfsigned.crt 12 | servercert=../cert/server/selfsigned.crt 13 | serverkey=../cert/server/selfsigned.key -------------------------------------------------------------------------------- /test/conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import redis 3 | 4 | from restapi_client import RESTAPI_client 5 | 6 | @pytest.fixture() 7 | def setup_restapi_client(): 8 | db = redis.StrictRedis('localhost', 6379, 0) 9 | db.flushdb() 10 | cache = redis.StrictRedis('localhost', 6379, 7) 11 | cache.flushdb() 12 | configdb = redis.StrictRedis('localhost', 6379, 4) 13 | configdb.flushdb() 14 | 15 | restapi_client = RESTAPI_client(db) 16 | restapi_client.post_config_restart_in_mem_db() 17 | 18 | # Sanity check 19 | keys = db.keys() 20 | assert keys == [] 21 | 22 | keys = cache.keys() 23 | assert keys == [] 24 | 25 | keys = configdb.keys() 26 | assert keys == [] 27 | 28 | yield db, cache, configdb, restapi_client 29 | -------------------------------------------------------------------------------- /test/requirements.txt: -------------------------------------------------------------------------------- 1 | redis==4.5.4 2 | requests==2.25.0 3 | -------------------------------------------------------------------------------- /test/restapi_client.py: -------------------------------------------------------------------------------- 1 | import json 2 | import logging 3 | import requests 4 | 5 | # DB Names 6 | ROUTE_TUN_TB = "_VNET_ROUTE_TUNNEL_TABLE" 7 | LOCAL_ROUTE_TB = "_VNET_ROUTE_TABLE" 8 | 9 | # DB Helper constants 10 | VNET_NAME_PREF = "Vnet" 11 | VLAN_NAME_PREF = "Vlan" 12 | 13 | TEST_HOST = 'http://localhost:8090/' 14 | 15 | class RESTAPI_client: 16 | 17 | def __init__(self, db): 18 | self.db = db 19 | 20 | maxDiff = None 21 | 22 | def post(self, url, body = []): 23 | if body == None: 24 | data = None 25 | else: 26 | data = json.dumps(body) 27 | 28 | logging.info("Request POST: %s" % url) 29 | logging.info("JSON Body: %s" % data) 30 | r = requests.post(TEST_HOST + url, data=data, headers={'Content-Type': 'application/json'}) 31 | logging.info('Response Code: %s' % r.status_code) 32 | logging.info('Response Body: %s' % r.text) 33 | return r 34 | 35 | def patch(self, url, body = []): 36 | if body == None: 37 | data = None 38 | else: 39 | data = json.dumps(body) 40 | 41 | logging.info("Request PATCH: %s" % url) 42 | logging.info("JSON Body: %s" % data) 43 | r = requests.patch(TEST_HOST + url, data=data, headers={'Content-Type': 'application/json'}) 44 | logging.info('Response Code: %s' % r.status_code) 45 | logging.info('Response Body: %s' % r.text) 46 | return r 47 | 48 | def get(self, url, body = [], params = {}): 49 | if body == None: 50 | data = None 51 | else: 52 | data = json.dumps(body) 53 | 54 | logging.info("Request GET: %s" % url) 55 | logging.info("JSON Body: %s" % data) 56 | r = requests.get(TEST_HOST + url, data=data, params=params, headers={'Content-Type': 'application/json'}) 57 | logging.info('Response Code: %s' % r.status_code) 58 | logging.info('Response Body: %s' % r.text) 59 | return r 60 | 61 | def delete(self, url, body = [], params = {}): 62 | if body == None: 63 | data = None 64 | else: 65 | data = json.dumps(body) 66 | 67 | logging.info("Request DELETE: %s" % url) 68 | logging.info("JSON Body: %s" % data) 69 | r = requests.delete(TEST_HOST + url, data=data, params=params, headers={'Content-Type': 'application/json'}) 70 | logging.info('Response Code: %s' % r.status_code) 71 | logging.info('Response Body: %s' % r.text) 72 | return r 73 | 74 | def get_config_reset_status(self): 75 | return self.get('v1/config/resetstatus') 76 | 77 | def post_config_reset_status(self, value): 78 | return self.post('v1/config/resetstatus', value) 79 | 80 | # BGP Community String 81 | def get_bgp_community_string(self, profile_name): 82 | return self.get('v1/config/bgp/profile/{profile_name}'.format(profile_name=profile_name)) 83 | 84 | def post_bgp_community_string(self, profile_name, value): 85 | return self.post('v1/config/bgp/profile/{profile_name}'.format(profile_name=profile_name), value) 86 | 87 | def delete_bgp_community_string(self, profile_name): 88 | return self.delete('v1/config/bgp/profile/{profile_name}'.format(profile_name=profile_name)) 89 | 90 | # VRF/VNET 91 | def post_config_vrouter_vrf_id(self, vrf_id, value): 92 | return self.post('v1/config/vrouter/{vrf_id}'.format(vrf_id=vrf_id), value) 93 | 94 | def get_config_vrouter_vrf_id(self, vrf_id): 95 | return self.get('v1/config/vrouter/{vrf_id}'.format(vrf_id=vrf_id)) 96 | 97 | def delete_config_vrouter_vrf_id(self, vrf_id): 98 | return self.delete('v1/config/vrouter/{vrf_id}'.format(vrf_id=vrf_id)) 99 | 100 | # Encap 101 | def post_config_tunnel_encap_vxlan_vnid(self, vnid, value): 102 | return self.post('v1/config/tunnel/encap/vxlan/{vnid}'.format(vnid=vnid), value) 103 | 104 | def delete_config_tunnel_encap_vxlan_vnid(self, vnid): 105 | return self.delete('v1/config/tunnel/encap/vxlan/{vnid}'.format(vnid=vnid)) 106 | 107 | def get_config_tunnel_encap_vxlan_vnid(self, vnid): 108 | return self.get('v1/config/tunnel/encap/vxlan/{vnid}'.format(vnid=vnid)) 109 | 110 | # Decap 111 | def post_config_tunnel_decap_tunnel_type(self, tunnel_type, value): 112 | return self.post('v1/config/tunnel/decap/{tunnel_type}'.format(tunnel_type=tunnel_type), value) 113 | 114 | def get_config_tunnel_decap_tunnel_type(self, tunnel_type): 115 | return self.get('v1/config/tunnel/decap/{tunnel_type}'.format(tunnel_type=tunnel_type)) 116 | 117 | def delete_config_tunnel_decap_tunnel_type(self, tunnel_type): 118 | return self.delete('v1/config/tunnel/decap/{tunnel_type}'.format(tunnel_type=tunnel_type)) 119 | 120 | # Vlan 121 | def post_config_vlan(self, vlan_id, value): 122 | return self.post('v1/config/interface/vlan/{vlan_id}'.format(vlan_id=vlan_id), value) 123 | 124 | def get_config_vlan(self, vlan_id): 125 | return self.get('v1/config/interface/vlan/{vlan_id}'.format(vlan_id=vlan_id)) 126 | 127 | def delete_config_vlan(self, vlan_id): 128 | return self.delete('v1/config/interface/vlan/{vlan_id}'.format(vlan_id=vlan_id)) 129 | 130 | def get_config_interface_vlans(self, vnet_id=None): 131 | params = {} 132 | if vnet_id != None: 133 | params['vnet_id'] = vnet_id 134 | return self.get('v1/config/interface/vlans',params=params) 135 | 136 | def get_config_vlans_all(self): 137 | return self.get('v1/config/interface/vlans/all') 138 | 139 | # Vlan Member 140 | def post_config_vlan_member(self, vlan_id, if_name, value): 141 | return self.post('v1/config/interface/vlan/{vlan_id}/member/{if_name}'.format(vlan_id=vlan_id, if_name=if_name), value) 142 | 143 | def get_config_vlan_member(self, vlan_id, if_name): 144 | return self.get('v1/config/interface/vlan/{vlan_id}/member/{if_name}'.format(vlan_id=vlan_id, if_name=if_name)) 145 | 146 | def delete_config_vlan_member(self, vlan_id, if_name): 147 | return self.delete('v1/config/interface/vlan/{vlan_id}/member/{if_name}'.format(vlan_id=vlan_id, if_name=if_name)) 148 | 149 | def get_config_interface_vlan_members(self, vlan_id): 150 | return self.get('v1/config/interface/vlan/{vlan_id}/members'.format(vlan_id=vlan_id)) 151 | 152 | def get_config_members_all(self): 153 | return self.get('v1/config/interface/vlans/members/all') 154 | 155 | # Vlan Neighbor 156 | def post_config_vlan_neighbor(self, vlan_id, ip_addr): 157 | return self.post('v1/config/interface/vlan/{vlan_id}/neighbor/{ip_addr}'.format(vlan_id=vlan_id, ip_addr=ip_addr)) 158 | 159 | def get_config_vlan_neighbor(self, vlan_id, ip_addr): 160 | return self.get('v1/config/interface/vlan/{vlan_id}/neighbor/{ip_addr}'.format(vlan_id=vlan_id, ip_addr=ip_addr)) 161 | 162 | def delete_config_vlan_neighbor(self, vlan_id, ip_addr): 163 | return self.delete('v1/config/interface/vlan/{vlan_id}/neighbor/{ip_addr}'.format(vlan_id=vlan_id, ip_addr=ip_addr)) 164 | 165 | def get_config_interface_vlan_neighbors(self, vlan_id): 166 | return self.get('v1/config/interface/vlan/{vlan_id}/neighbors'.format(vlan_id=vlan_id)) 167 | 168 | # Routes 169 | def patch_config_vrouter_vrf_id_routes(self, vrf_id, value): 170 | return self.patch('v1/config/vrouter/{vrf_id}/routes'.format(vrf_id=vrf_id), value) 171 | 172 | def patch_config_vrf_vrf_id_routes(self, vrf_id, value): 173 | return self.patch('v1/config/vrf/{vrf_id}/routes'.format(vrf_id=vrf_id), value) 174 | 175 | def delete_config_vrouter_vrf_id_routes(self, vrf_id, vnid=None, value=None): 176 | params = {} 177 | if vnid != None: 178 | params['vnid'] = vnid 179 | return self.delete('v1/config/vrouter/{vrf_id}/routes'.format(vrf_id=vrf_id), value, params=params) 180 | 181 | def get_config_vrouter_vrf_id_routes(self, vrf_id, vnid=None, ip_prefix=None): 182 | params = {} 183 | if vnid != None: 184 | params['vnid'] = vnid 185 | if ip_prefix != None: 186 | params['ip_prefix'] = ip_prefix 187 | return self.get('v1/config/vrouter/{vrf_id}/routes'.format(vrf_id=vrf_id), params=params) 188 | 189 | def get_config_vrf_vrf_id_routes(self, vrf_id, ip_prefix=None): 190 | params = {} 191 | if ip_prefix != None: 192 | params['ip_prefix'] = ip_prefix 193 | return self.get('v1/config/vrf/{vrf_id}/routes'.format(vrf_id=vrf_id), params=params) 194 | 195 | # Static Route Expiry Timer 196 | def get_rt_expiry_timer(self): 197 | return self.get('v1/config/vrf/route_expiry') 198 | 199 | def post_rt_expiry_timer(self, value): 200 | return self.post('v1/config/vrf/route_expiry', value) 201 | 202 | # In memory DB restart 203 | def post_config_restart_in_mem_db(self): 204 | return self.post('v1/config/restartdb') 205 | 206 | # Operations 207 | # Ping 208 | def post_ping(self, value): 209 | return self.post('v1/operations/ping', value) 210 | 211 | # Helper functions 212 | def post_generic_vxlan_tunnel(self): 213 | rv = self.post_config_tunnel_decap_tunnel_type('vxlan', { 214 | 'ip_addr': '34.53.1.0' 215 | }) 216 | assert rv.status_code == 204 217 | 218 | def post_generic_vxlan_v6_tunnel(self): 219 | rv = self.post_config_tunnel_decap_tunnel_type('vxlan', { 220 | 'ip_addr': '2000::1000' 221 | }) 222 | assert rv.status_code == 204 223 | 224 | def post_generic_default_vrouter_and_deps(self): 225 | self.post_generic_vxlan_tunnel() 226 | self.post_generic_vxlan_v6_tunnel() 227 | rv = self.post_config_vrouter_vrf_id("Vnet-default", { 228 | 'vnid': 8000 229 | }) 230 | assert rv.status_code == 204 231 | 232 | rv = self.post_config_vrouter_vrf_id("Vnet-default-v4", { 233 | 'vnid': 8000 234 | }) 235 | assert rv.status_code == 204 236 | 237 | def post_generic_vrouter_and_deps(self): 238 | self.post_generic_vxlan_tunnel() 239 | rv = self.post_config_vrouter_vrf_id("vnet-guid-1", { 240 | 'vnid': 1001 241 | }) 242 | assert rv.status_code == 204 243 | 244 | def post_generic_vrouter_and_deps_duplicate(self): 245 | self.post_generic_vxlan_tunnel() 246 | rv = self.post_config_vrouter_vrf_id("vnet-guid-1", { 247 | 'vnid': 1001 248 | }) 249 | assert rv.status_code == 204 250 | rv = self.post_config_vrouter_vrf_id("vnet-guid-10", { 251 | 'vnid': 1001 252 | }) 253 | assert rv.status_code == 409 254 | rv = self.post_config_vrouter_vrf_id("vnet-guid-1", { 255 | 'vnid': 1001 256 | }) 257 | assert rv.status_code == 409 258 | rv = self.post_config_vrouter_vrf_id("vnet-guid-1", { 259 | 'vnid': 2001 260 | }) 261 | assert rv.status_code == 409 262 | 263 | def post_generic_vlan_and_deps(self): 264 | self.post_generic_vrouter_and_deps() 265 | rv = self.post_config_vlan(2, {'vnet_id' : 'vnet-guid-1', 'ip_prefix' : '10.1.1.0/24'}) 266 | assert rv.status_code == 204 267 | 268 | def check_routes_exist_in_tun_tb(self, vnet_num_mapped, routes_arr): 269 | for route in routes_arr: 270 | route_table = self.db.hgetall(ROUTE_TUN_TB + ':' + VNET_NAME_PREF +str(vnet_num_mapped)+':'+route['ip_prefix']) 271 | assert route_table == { 272 | b'endpoint' : route['nexthop'].encode(), 273 | b'mac_address' : route['mac_address'].encode(), 274 | b'vni' : str(route['vnid']).encode() 275 | } 276 | 277 | def check_routes_dont_exist_in_tun_tb(self, vnet_num_mapped, routes_arr): 278 | for route in routes_arr: 279 | route_table = self.db.hgetall(ROUTE_TUN_TB + ':' + VNET_NAME_PREF +str(vnet_num_mapped)+':'+route['ip_prefix']) 280 | assert route_table == {} 281 | 282 | def check_routes_exist_in_loc_route_tb(self, vnet_num_mapped, routes_arr): 283 | for route in routes_arr: 284 | route_table = self.db.hgetall(LOCAL_ROUTE_TB + ':' + VNET_NAME_PREF +str(vnet_num_mapped)+':'+route['ip_prefix']) 285 | assert route_table == { 286 | b'nexthop' : route['nexthop'].encode(), 287 | b'ifname' : route['ifname'].encode() 288 | } 289 | 290 | def check_routes_dont_exist_in_loc_route_tb(self, vnet_num_mapped, routes_arr): 291 | for route in routes_arr: 292 | route_table = self.db.hgetall(LOCAL_ROUTE_TB + ':' + VNET_NAME_PREF +str(vnet_num_mapped)+':'+route['ip_prefix']) 293 | assert route_table == {} 294 | --------------------------------------------------------------------------------