Skip to content

Commit

Permalink
add mock HTTP server
Browse files Browse the repository at this point in the history
  • Loading branch information
wg committed Nov 3, 2016
1 parent 8cffaf6 commit 7b4ff63
Show file tree
Hide file tree
Showing 33 changed files with 5,256 additions and 39 deletions.
16 changes: 8 additions & 8 deletions demo.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@
int main(int argc, char **argv) {
int r;
kflowConfig cfg = {
.URL = "http://chdev:20012/chf",
.URL = "http://127.0.0.1:8999/chf",
.API = {
.email = "will@kentik.com",
.token = "81b7262feceecc94eef3ddafbc2c152f",
.URL = "http://chdev:8080/api/v5",
.email = "test@example.com",
.token = "token",
.URL = "http://127.0.0.1:8999/api/v5",
},
.device_id = 1001,
.device_id = 1,
.verbose = 1,
};

if ((r = kflowInit(&cfg)) != 0) {
printf("error initializing libkflow: %d", r);
printf("error initializing libkflow: %d\n", r);
exit(1);
};

Expand All @@ -30,12 +30,12 @@ int main(int argc, char **argv) {
};

if ((r = kflowSend(&flow)) != 0) {
printf("error sending flow: %d", r);
printf("error sending flow: %d\n", r);
exit(1);
}

if ((r = kflowStop(10*1000)) != 0) {
printf("error stopping libkflow: %d", r);
printf("error stopping libkflow: %d\n", r);
exit(1);
}

Expand Down
96 changes: 96 additions & 0 deletions src/github.com/kentik/libkflow/api/server/print.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package server

import (
"bytes"
"fmt"
"log"
"net"
"text/tabwriter"

"github.com/kentik/libkflow/chf"
)

func Print(i int, flow chf.CHF) {
buf := bytes.Buffer{}
w := tabwriter.NewWriter(&buf, 0, 4, 1, ' ', 0)

fmt.Fprintf(w, "FLOW #%02d\n", i)
fmt.Fprintf(w, " timestampNano:\t%v\n", flow.TimestampNano())
fmt.Fprintf(w, " dstAs:\t%v\n", flow.DstAs())
fmt.Fprintf(w, " dstGeo:\t%v\n", flow.DstGeo())
fmt.Fprintf(w, " dstMac:\t%v\n", flow.DstMac())
fmt.Fprintf(w, " headerLen:\t%v\n", flow.HeaderLen())
fmt.Fprintf(w, " inBytes:\t%v\n", flow.InBytes())
fmt.Fprintf(w, " inPkts:\t%v\n", flow.InPkts())
fmt.Fprintf(w, " inputPort:\t%v\n", flow.InputPort())
fmt.Fprintf(w, " ipSize:\t%v\n", flow.IpSize())
fmt.Fprintf(w, " ipv4DstAddr:\t%v\n", ip(flow.Ipv4DstAddr()))
fmt.Fprintf(w, " ipv4SrcAddr:\t%v\n", ip(flow.Ipv4SrcAddr()))
fmt.Fprintf(w, " l4DstPort:\t%v\n", flow.L4DstPort())
fmt.Fprintf(w, " l4SrcPort:\t%v\n", flow.L4SrcPort())
fmt.Fprintf(w, " outputPort:\t%v\n", flow.OutputPort())
fmt.Fprintf(w, " protocol:\t%v\n", flow.Protocol())
fmt.Fprintf(w, " sampledPacketSize:\t%v\n", flow.SampledPacketSize())
fmt.Fprintf(w, " srcAs:\t%v\n", flow.SrcAs())
fmt.Fprintf(w, " srcGeo:\t%v\n", flow.SrcGeo())
fmt.Fprintf(w, " srcMac:\t%v\n", flow.SrcMac())
fmt.Fprintf(w, " tcpFlags:\t%v\n", flow.TcpFlags())
fmt.Fprintf(w, " tos:\t%v\n", flow.Tos())
fmt.Fprintf(w, " vlanIn:\t%v\n", flow.VlanIn())
fmt.Fprintf(w, " vlanOut:\t%v\n", flow.VlanOut())
fmt.Fprintf(w, " ipv4NextHop:\t%v\n", ip(flow.Ipv4NextHop()))
fmt.Fprintf(w, " mplsType:\t%v\n", flow.MplsType())
fmt.Fprintf(w, " outBytes:\t%v\n", flow.OutBytes())
fmt.Fprintf(w, " outPkts:\t%v\n", flow.OutPkts())
fmt.Fprintf(w, " tcpRetransmit:\t%v\n", flow.TcpRetransmit())
fmt.Fprintf(w, " srcFlowTags:\t%#v\n", str(flow.SrcFlowTags()))
fmt.Fprintf(w, " dstFlowTags:\t%#v\n", str(flow.DstFlowTags()))
fmt.Fprintf(w, " sampleRate:\t%v\n", flow.SampleRate())
fmt.Fprintf(w, " deviceId:\t%v\n", flow.DeviceId())
fmt.Fprintf(w, " flowTags:\t%#v\n", str(flow.FlowTags()))
fmt.Fprintf(w, " timestamp:\t%v\n", flow.Timestamp())
fmt.Fprintf(w, " dstBgpAsPath:\t%#v\n", str(flow.DstBgpAsPath()))
fmt.Fprintf(w, " dstBgpCommunity:\t%#v\n", str(flow.DstBgpCommunity()))
fmt.Fprintf(w, " srcBgpAsPath:\t%#v\n", str(flow.SrcBgpAsPath()))
fmt.Fprintf(w, " srcBgpCommunity:\t%#v\n", str(flow.SrcBgpCommunity()))
fmt.Fprintf(w, " srcNextHopAs:\t%v\n", flow.SrcNextHopAs())
fmt.Fprintf(w, " dstNextHopAs:\t%v\n", flow.DstNextHopAs())
fmt.Fprintf(w, " srcGeoRegion:\t%v\n", flow.SrcGeoRegion())
fmt.Fprintf(w, " dstGeoRegion:\t%v\n", flow.DstGeoRegion())
fmt.Fprintf(w, " srcGeoCity:\t%v\n", flow.SrcGeoCity())
fmt.Fprintf(w, " dstGeoCity:\t%v\n", flow.DstGeoCity())
fmt.Fprintf(w, " big:\t%v\n", flow.Big())
fmt.Fprintf(w, " sampleAdj:\t%v\n", flow.SampleAdj())
fmt.Fprintf(w, " ipv4DstNextHop:\t%v\n", ip(flow.Ipv4DstNextHop()))
fmt.Fprintf(w, " ipv4SrcNextHop:\t%v\n", ip(flow.Ipv4SrcNextHop()))
fmt.Fprintf(w, " srcRoutePrefix:\t%v\n", flow.SrcRoutePrefix())
fmt.Fprintf(w, " dstRoutePrefix:\t%v\n", flow.DstRoutePrefix())
fmt.Fprintf(w, " srcRouteLength:\t%v\n", flow.SrcRouteLength())
fmt.Fprintf(w, " dstRouteLength:\t%v\n", flow.DstRouteLength())
fmt.Fprintf(w, " srcSecondAsn:\t%v\n", flow.SrcSecondAsn())
fmt.Fprintf(w, " dstSecondAsn:\t%v\n", flow.DstSecondAsn())
fmt.Fprintf(w, " srcThirdAsn:\t%v\n", flow.SrcThirdAsn())
fmt.Fprintf(w, " dstThirdAsn:\t%v\n", flow.DstThirdAsn())
fmt.Fprintf(w, " ipv6DstAddr:\t%v\n", ip(flow.Ipv6DstAddr()))
fmt.Fprintf(w, " ipv6SrcAddr:\t%v\n", ip(flow.Ipv6SrcAddr()))
fmt.Fprintf(w, " srcEthMac:\t%v\n", flow.SrcEthMac())
fmt.Fprintf(w, " dstEthMac:\t%v\n", flow.DstEthMac())
w.Flush()

log.Output(0, buf.String())
}

func ip(v interface{}, _ ...error) net.IP {
switch v := v.(type) {
case uint32:
return net.IPv4(byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
case []byte:
return net.IP(v)
default:
return (net.IP)(nil)
}
}

func str(v interface{}, _ error) interface{} {
return v
}
140 changes: 140 additions & 0 deletions src/github.com/kentik/libkflow/api/server/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package server

import (
"encoding/json"
"fmt"
"net"
"net/http"
"strconv"

"github.com/kentik/libkflow/api"
"github.com/kentik/libkflow/chf"
"zombiezen.com/go/capnproto2"
)

type Server struct {
Host net.IP
Port int
Email string
Token string
Device api.Device
mux *http.ServeMux
listener net.Listener
}

func New(host string, port int) (*Server, error) {
addr, err := net.ResolveTCPAddr("tcp", net.JoinHostPort(host, strconv.Itoa(port)))
if err != nil {
return nil, err
}

listener, err := net.ListenTCP("tcp", addr)
if err != nil {
return nil, err
}

addr = listener.Addr().(*net.TCPAddr)

return &Server{
Host: addr.IP,
Port: addr.Port,
mux: http.NewServeMux(),
listener: listener,
}, nil
}

func (s *Server) Serve(email, token string, dev api.Device) error {
s.Email = email
s.Token = token
s.Device = dev
s.mux.HandleFunc("/api/v5/device/", s.wrap(s.device))
s.mux.HandleFunc("/chf", s.wrap(s.flow))
return http.Serve(s.listener, s.mux)
}

func (s *Server) device(w http.ResponseWriter, r *http.Request) {
var did int

n, err := fmt.Sscanf(r.URL.Path, "/api/v5/device/%d", &did)
if n != 1 || err != nil {
panic(http.StatusBadRequest)
}

if did != s.Device.ID {
panic(http.StatusNotFound)
}

w.Header().Set("Content-Type", "application/json")
err = json.NewEncoder(w).Encode(&api.DeviceResponse{
Device: s.Device,
})

if err != nil {
panic(http.StatusInternalServerError)
}
}

func (s *Server) flow(w http.ResponseWriter, r *http.Request) {
if r.FormValue("sid") != "0" {
panic(http.StatusBadRequest)
}

if r.FormValue("sender_id") != s.Device.ClientID() {
panic(http.StatusBadRequest)
}

cid := [80]byte{}
n, err := r.Body.Read(cid[:])
if err != nil || n != len(cid) {
panic(http.StatusBadRequest)
}

msg, err := capnp.NewPackedDecoder(r.Body).Decode()
defer r.Body.Close()
if err != nil {
panic(http.StatusBadRequest)
}

root, err := chf.ReadRootPackedCHF(msg)
if err != nil {
panic(http.StatusBadRequest)
}

msgs, err := root.Msgs()
if err != nil {
panic(http.StatusBadRequest)
}

for i := 0; i < msgs.Len(); i++ {
Print(i, msgs.At(i))
}
}

func (s *Server) wrap(f handler) handler {
return func(w http.ResponseWriter, r *http.Request) {
defer func() {
if r := recover(); r != nil {
if code, ok := r.(int); ok {
http.Error(w, http.StatusText(code), code)
return
}
panic(r)
}
}()

email := r.Header.Get("X-CH-Auth-Email")
token := r.Header.Get("X-CH-Auth-API-Token")

if email != s.Email || token != s.Token {
panic(http.StatusUnauthorized)
}

if err := r.ParseForm(); err != nil {
panic(http.StatusBadRequest)
}

f(w, r)
}
}

type handler func(http.ResponseWriter, *http.Request)
6 changes: 3 additions & 3 deletions src/github.com/kentik/libkflow/api/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,16 @@ type DeviceResponse struct {
}

type Device struct {
ID string `json:"id"`
ID int `json:"id,string"`
Name string `json:"device_name"`
CompanyID string `json:"company_id"`
CompanyID int `json:"company_id,string"`
Custom CustomColumns `json:"custom_columns"`
}

type CustomColumns map[string]uint64

func (d *Device) ClientID() string {
return fmt.Sprintf("%s:%s:%s", d.CompanyID, d.Name, d.ID)
return fmt.Sprintf("%d:%s:%d", d.CompanyID, d.Name, d.ID)
}

func (c *CustomColumns) UnmarshalJSON(data []byte) error {
Expand Down
60 changes: 60 additions & 0 deletions src/github.com/kentik/libkflow/cmd/server/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package main

import (
"log"
"os"

"github.com/jessevdk/go-flags"
"github.com/kentik/libkflow/api"
"github.com/kentik/libkflow/api/server"
)

type Args struct {
Host string `short:"h" description:"listen on host"`
Port int `short:"p" description:"listen on port"`
Email string `long:"email" description:"API auth email"`
Token string `long:"token" description:"API auth token"`
CompanyID int `long:"company-id" description:"company ID "`
DeviceID int `long:"device-id" description:"device ID "`
DeviceName string `long:"device-name" description:"device name "`
}

func main() {
args := Args{
Host: "127.0.0.1",
Port: 8999,
Email: "[email protected]",
Token: "token",
CompanyID: 1,
DeviceID: 1,
DeviceName: "dev1",
}

parser := flags.NewParser(&args, flags.PassDoubleDash|flags.HelpFlag)
if _, err := parser.Parse(); err != nil {
switch err.(*flags.Error).Type {
case flags.ErrHelp:
parser.WriteHelp(os.Stderr)
os.Exit(1)
default:
log.Fatal(err)
}
}

s, err := server.New(args.Host, args.Port)
if err != nil {
log.Fatal(err)
}

log.Printf("listening on %s:%d", s.Host, s.Port)

err = s.Serve(args.Email, args.Token, api.Device{
ID: args.DeviceID,
Name: args.DeviceName,
CompanyID: args.CompanyID,
})

if err != nil {
log.Fatal(err)
}
}
10 changes: 5 additions & 5 deletions src/github.com/kentik/libkflow/kflow.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,18 @@ typedef struct {
uint32_t dstAs; // destination AS
uint32_t dstGeo; // IGNORE
uint32_t dstMac; // IGNORE
uint32_t headerLen; //
uint32_t headerLen; // IGNORE
uint64_t inBytes; // number of bytes in
uint64_t inPkts; // number of packets in
uint32_t inputPort; // input interface identifier
uint32_t ipSize; //
uint32_t ipSize; // IGNORE
uint32_t ipv4DstAddr; // IPv4 dst address
uint32_t ipv4SrcAddr; // IPv6 src address
uint32_t ipv4SrcAddr; // IPv4 src address
uint32_t l4DstPort; // layer 4 dst port
uint32_t l4SrcPort; // layer 4 src port
uint32_t outputPort; // output interface identifier
uint32_t protocol; // IP protocol number
uint32_t sampledPacketSize; //
uint32_t sampledPacketSize; // IGNORE
uint32_t srcAs; // source AS
uint32_t srcGeo; // IGNORE
uint32_t srcMac; // IGNORE
Expand All @@ -45,7 +45,7 @@ typedef struct {
uint32_t vlanIn; // input VLAN number
uint32_t vlanOut; // output VLAN number
uint32_t ipv4NextHop; // IPv4 next-hop address
uint32_t mplsType; //
uint32_t mplsType; // IGNORE
uint64_t outBytes; // number of bytes out
uint64_t outPkts; // number of packets out
uint32_t tcpRetransmit; // number of packets retransmitted
Expand Down
Loading

0 comments on commit 7b4ff63

Please sign in to comment.