├── .gitignore ├── .gitmodules ├── LICENSE ├── Makefile ├── README.md ├── example ├── ISO15118-4_tests │ ├── evcc_tests │ │ ├── Makefile │ │ ├── chargeparamdiscovery_test.c │ │ ├── chargingstatus_req.c │ │ ├── evcc_test │ │ ├── evcc_test.c │ │ ├── evcctests.h │ │ ├── powerdelivery_test.c │ │ ├── servicedetailandpayment_test.c │ │ └── servicediscovery_test.c │ └── secc_tests │ │ ├── Makefile │ │ ├── powerdeliveryres_test.c │ │ ├── secc_test │ │ └── secc_test.c ├── Makefile ├── bash │ ├── connect │ ├── deploy │ └── rsync-exclude.txt ├── certs │ ├── .key │ ├── 01.pem │ ├── 02.pem │ ├── 03.pem │ ├── ca │ │ ├── mobilityop │ │ │ ├── index.txt │ │ │ ├── index.txt.attr │ │ │ ├── index.txt.attr.old │ │ │ ├── index.txt.old │ │ │ ├── openssl.cfg │ │ │ ├── serial │ │ │ └── serial.old │ │ ├── oem │ │ │ ├── index.txt │ │ │ ├── index.txt.attr │ │ │ ├── openssl.cfg │ │ │ └── serial │ │ └── v2g │ │ │ ├── index.txt │ │ │ ├── index.txt.attr │ │ │ ├── openssl.cfg │ │ │ └── serial │ ├── contract.csr │ ├── contract.key │ ├── contract.pem │ ├── contractchain.pem │ ├── ev.csr │ ├── ev.key │ ├── ev.pem │ ├── evse (copy).pem │ ├── evse.csr │ ├── evse.key │ ├── evse.pem │ ├── index.txt │ ├── nano.save │ ├── readme.txt │ └── root │ │ ├── mobilityop │ │ ├── certs │ │ │ └── mobilityop.pem │ │ ├── index.txt │ │ ├── index.txt.save │ │ ├── keys │ │ │ └── mobilityop.key │ │ └── openssl.cfg │ │ ├── oem │ │ ├── certs │ │ │ └── oem.pem │ │ ├── index.txt │ │ ├── index.txt.save │ │ ├── keys │ │ │ └── oem.key │ │ └── openssl.cfg │ │ └── v2g │ │ ├── certs │ │ └── v2g.pem │ │ ├── index.txt │ │ ├── index.txt.save │ │ ├── keys │ │ └── v2g.key │ │ └── openssl.cfg ├── client.c ├── client.h ├── server.c ├── server.h ├── slac │ ├── homeplug.h │ ├── plc_eth.c │ ├── plc_eth.h │ ├── powerlinegp_evse_slac.c │ ├── qualcomm_slac.h │ ├── slacassoc.c │ └── slacassoc.h ├── test.c ├── timeprofiling.c └── timeprofiling.h ├── map.c ├── map.h ├── nikolav2g.h ├── sdp.c ├── session.c └── v2gconn.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # Executables 24 | *.exe 25 | *.out 26 | *.app 27 | *.i*86 28 | *.x86_64 29 | *.hex 30 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "utils/libmultitask"] 2 | path = utils/libmultitask 3 | url = https://github.com/zhasha/libmultitask.git 4 | [submodule "utils/OpenV2G_0.9.3/OpenV2G-nikolav2g"] 5 | path = utils/OpenV2G_0.9.3/OpenV2G-nikolav2g 6 | url = https://github.com/zhasha/OpenV2G-nikolav2g.git 7 | [submodule "utils/OpenV2G-nikolav2g"] 8 | path = utils/OpenV2G-nikolav2g 9 | url = https://github.com/zhasha/OpenV2G-nikolav2g.git 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, cblach <> 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC=clang 2 | AR=ar 3 | MAKE=make 4 | CFLAGS=-g -Os -Wall -pedantic 5 | 6 | PREFIX=/usr/local 7 | LIBDIR=$(PREFIX)/lib 8 | INCDIR=$(PREFIX)/include 9 | 10 | TARGET=libnikolav2g.a 11 | HEADER=nikolav2g.h 12 | 13 | SOURCES=$(wildcard *.c) 14 | OBJECTS=$(SOURCES:.c=.o) 15 | 16 | all: lib example 17 | 18 | lib: $(TARGET) 19 | 20 | example: lib 21 | $(MAKE) -C example 22 | 23 | $(TARGET): $(OBJECTS) 24 | $(AR) rcs $(TARGET) $(OBJECTS) 25 | 26 | $(OBJECTS): $(HEADER) 27 | 28 | .c.o: 29 | $(CC) $(CFLAGS) -c -o $@ $< 30 | 31 | install: $(TARGET) 32 | install -D $(TARGET) $(DESTDIR)$(LIBDIR)/$(TARGET) 33 | install -D $(HEADER) $(DESTDIR)$(INCDIR)/$(HEADER) 34 | 35 | clean: 36 | rm -f $(OBJECTS) $(TARGET) 37 | $(MAKE) -C example clean 38 | 39 | .PHONY: all lib example install clean 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nikola-v2gstack 2 | Open source ISO 15118 communication implementation for Linux made for my Master Thesis at DTU Risø. 3 | Please note that it requires uses TLS/PolarSSL, OpenV2GStack and libmultitask. The stack is compatible with the amd64 and armv7 architectures. 4 | 5 | Getting started: 6 | - Download mbed TLS from https://tls.mbed.org/download or their git repository 7 | - mbed TLS must be configured to use thread locks. This is done by uncommenting #define POLARSSL_THREADING_C and POLARSSL_THREADING_PTHREAD in include/polarssl/config.h. 8 | - *make && make check && sudo make install* the mbed TLS libary 9 | - *make && sudo make install* the OpenV2G library in utils/OpenV2g_x.x.x 10 | - *make && sudo make install* the libmultitask library in utils/libmultitask/unix 11 | - *make && sudo make install* the nikola v2g stack 12 | 13 | Things to note: 14 | - I have made a custom makefile for OpenV2G. It can be located in utils/OpenV2G/Makefile 15 | - The SLAC client implementation is programmed to work with the Qualcomm QCA7000 chip. 16 | - The SLAC server is programmed to work with the Insys Powerline GP with SLAC device. 17 | - The application layer messaging in the example folder only covers a reference AC implementation. 18 | 19 | Bugs are still likely to be present. Please do not hesitate to create issues. Also you may have to download a stdatomic.h from LLVM if it's not in your current installation. 20 | 21 | 22 | Working test bench setup: 23 | 24 | Beaglebone <-Ethernet-> Insys Powerline GP with SLAC <-Power Line-> Insys Powerline without SLAC (QCA7000 based) <-Ethernet-> Beaglebone 25 | 26 | 27 | A lot of credit to Joakim Sindholt for making his brilliant C multithreading library libmultitask, see https://github.com/zhasha/libmultitask. 28 | Also thanks to Siemens and Insys for making OpenV2g, see http://openv2g.sourceforge.net/. 29 | -------------------------------------------------------------------------------- /example/ISO15118-4_tests/evcc_tests/Makefile: -------------------------------------------------------------------------------- 1 | CC=clang 2 | LD=clang 3 | CFLAGS=-g -Os -Wall -pedantic 4 | LDFLAGS= 5 | 6 | TARGET=evcc_test 7 | 8 | SOURCES=\ 9 | ../../server.c\ 10 | $(wildcard *.c)\ 11 | 12 | HEADERS=\ 13 | 14 | OBJECTS=\ 15 | $(SOURCES:.c=.o)\ 16 | 17 | LIBS=\ 18 | -L../..\ 19 | -lnikolav2g\ 20 | -lmultitask\ 21 | -lOpenV2G\ 22 | -lpolarssl\ 23 | -lm\ 24 | -lrt\ 25 | -lpthread\ 26 | 27 | INCLUDES=-I../..\ 28 | 29 | .c.o: 30 | $(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $< 31 | 32 | $(TARGET): $(OBJECTS) 33 | $(LD) $(LDFLAGS) -o $(TARGET) $(OBJECTS) $(LIBS) 34 | 35 | clean: 36 | rm -f $(OBJECTS) $(TARGET) 37 | 38 | all: $(TARGET) 39 | -------------------------------------------------------------------------------- /example/ISO15118-4_tests/evcc_tests/chargeparamdiscovery_test.c: -------------------------------------------------------------------------------- 1 | #include "evcctests.h" 2 | 3 | int charge_parameter_discovery_test( 4 | struct v2gEXIDocument *exiIn, 5 | struct v2gEXIDocument *exiOut, 6 | session_t *s, session_data_t *sd) 7 | 8 | { 9 | static int step = 0; 10 | int ret = TEST_IGNORE; 11 | switch (step) { 12 | case 0: 13 | if (exiIn->V2G_Message.Body.AuthorizationReq_isUsed) { 14 | exiOut->V2G_Message.Body.AuthorizationRes_isUsed = 1u; 15 | init_v2gAuthorizationResType(&exiOut->V2G_Message.Body.AuthorizationRes); 16 | if (handle_authorization(exiIn, exiOut, s, sd) != 0) { 17 | printf("Test %d Failed: Auth request\n", ntest); 18 | exit(-1); 19 | } 20 | step = 1; 21 | } 22 | break; 23 | case 1: 24 | if (exiIn->V2G_Message.Body.ChargeParameterDiscoveryReq_isUsed) { 25 | if (handle_charge_parameters(exiIn, exiOut, s, sd) != 0) { 26 | printf("Test %d Failed: Invalid request\n", ntest); 27 | exit(-1); 28 | } 29 | } else { 30 | printf("Test %d Failed: Unexpected Request\n", ntest); 31 | exit(-1); 32 | } 33 | printf("Test %d Succesful: Charge Parameter Request Received\n", ntest); 34 | succeses++; 35 | ret = TEST_CLOSE_CONNECTION; 36 | step = 0; 37 | ntest++; 38 | break; 39 | } 40 | return ret; 41 | } 42 | -------------------------------------------------------------------------------- /example/ISO15118-4_tests/evcc_tests/chargingstatus_req.c: -------------------------------------------------------------------------------- 1 | #include "evcctests.h" 2 | 3 | int charging_status_test( 4 | struct v2gEXIDocument *exiIn, 5 | struct v2gEXIDocument *exiOut, 6 | session_t *s, session_data_t *sd) 7 | 8 | { 9 | static int step = 0; 10 | int ret = TEST_IGNORE; 11 | switch (step) { 12 | case 0: 13 | if (exiIn->V2G_Message.Body.PowerDeliveryReq_isUsed) { 14 | exiOut->V2G_Message.Body.PowerDeliveryRes_isUsed = 1u; 15 | init_v2gPowerDeliveryResType(&exiOut->V2G_Message.Body.PowerDeliveryRes); 16 | if (handle_power_delivery(exiIn, exiOut, s, sd) != 0) { 17 | printf("Test %d Failed: Charge Param initial request required\n", ntest); 18 | exit(-1); 19 | } 20 | step = 1; 21 | } 22 | break; 23 | case 1: 24 | if (exiIn->V2G_Message.Body.ChargingStatusReq_isUsed) { 25 | if (s == NULL) { 26 | printf("Test %d Failed: NO session\n", ntest); 27 | } 28 | } else { 29 | printf("Test %d Failed: Unexpected Request\n", ntest); 30 | exit(-1); 31 | } 32 | printf("Test %d Succesful: Power Delivery Request Received\n", ntest); 33 | succeses++; 34 | ret = TEST_CLOSE_CONNECTION; 35 | step = 0; 36 | ntest++; 37 | break; 38 | } 39 | return ret; 40 | } 41 | -------------------------------------------------------------------------------- /example/ISO15118-4_tests/evcc_tests/evcc_test: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cblach/nikola-v2gstack/5609b011fd8a4edfa27ee9afabcd7251dc4bcece/example/ISO15118-4_tests/evcc_tests/evcc_test -------------------------------------------------------------------------------- /example/ISO15118-4_tests/evcc_tests/evcc_test.c: -------------------------------------------------------------------------------- 1 | #include "evcctests.h" 2 | 3 | 4 | int succeses = 0, ntest = 1; 5 | 6 | static const char *argv0; 7 | void usage(void) 8 | { 9 | fprintf(stderr, "Usage: %s [-sv] [--] interface node-type\n", argv0); 10 | exit(1); 11 | } 12 | 13 | 14 | 15 | void session_setup_test(struct v2gEXIDocument *exiIn) 16 | { 17 | uint64_t sessionid; 18 | if (!exiIn->V2G_Message.Body.SessionSetupReq_isUsed) { 19 | printf("Test %d failed, unexpected request type\n", ntest); 20 | return; 21 | } 22 | memcpy(&sessionid, exiIn->V2G_Message.Header.SessionID.bytes, 8); 23 | if (sessionid != 0) { 24 | printf("Test %d failed, initial session id non-zero\n", ntest); 25 | return; 26 | } 27 | printf("Test %d: Session setup, succesful\n", ntest); 28 | succeses++; 29 | } 30 | 31 | 32 | int tests(struct v2gEXIDocument *exiIn, 33 | struct v2gEXIDocument *exiOut, 34 | session_t *s, session_data_t *sd) { 35 | int ret = TEST_IGNORE; 36 | switch (ntest) { 37 | case TC_EVCC_CMN_VTB_SessionSetup_001: 38 | session_setup_test(exiIn); 39 | ret = TEST_CLOSE_CONNECTION; 40 | ntest++; 41 | break; 42 | case TC_EVCC_CMN_VTB_ServiceDiscovery_001: 43 | if (exiIn->V2G_Message.Body.ServiceDiscoveryReq_isUsed) { 44 | service_discovery_test1(exiIn, exiOut, s, sd); 45 | ret = TEST_CLOSE_CONNECTION; 46 | ntest++; 47 | } 48 | break; 49 | case TC_EVCC_CMN_VTB_ServiceDiscovery_002: 50 | case TC_EVCC_CMN_VTB_ServiceDiscovery_003: 51 | case TC_EVCC_CMN_VTB_ServiceDiscovery_004: 52 | ret = service_discovery_test_sequence(exiIn, exiOut, s, sd); 53 | break; 54 | case TC_EVCC_CMN_VTB_ServiceDetailAndPaymentSelection_001: 55 | ret = service_detail_and_payment_selection_testseq(exiIn, exiOut, s, sd); 56 | break; 57 | case TC_EVCC_CMN_VTB_ChargeParameterDiscovery_001: 58 | ret = charge_parameter_discovery_test(exiIn, exiOut, s, sd); 59 | break; 60 | case TC_EVCC_AC_VTB_PowerDelivery_001: 61 | ret = power_delivery_test(exiIn, exiOut, s, sd); 62 | break; 63 | case TC_EVCC_AC_VTB_ChargingStatus_001: 64 | ret = charging_status_test(exiIn, exiOut, s, sd); 65 | break; 66 | default: 67 | printf("Done testing, %d/%d tests succesful\n", succeses, ntest - 1); 68 | exit(0); 69 | } 70 | return ret; 71 | } 72 | int create_response_message_evcctest(struct v2gEXIDocument *exiIn, struct v2gEXIDocument *exiOut, bool tls_enabled) { 73 | int err = -1, exit = 0;//ERROR_UNEXPECTED_REQUEST_MESSAGE; 74 | session_t *s; 75 | session_data_t *sd; 76 | // Test 1 completed if we have gotten this far (i.e. correct application handshake 77 | 78 | /* create response message as EXI document */ 79 | if (!exiIn->V2G_Message_isUsed) { 80 | printf("V2GMessage not used\n"); 81 | return -1; 82 | } 83 | 84 | // === Fetch the session === 85 | if (exiIn->V2G_Message.Body.SessionSetupReq_isUsed) { 86 | s = session_new(sizeof(session_data_t), &session_data_cleanup); 87 | } else { 88 | s = session_lookup_exi(exiIn); 89 | } 90 | // Note that the session can be NULL, thus this must be 91 | // check in the individual response functions 92 | sd = (session_data_t*)&s->data; 93 | // === Inititialize request handling === 94 | session_lock(s); 95 | init_v2g_response(exiOut, s); 96 | // === Start request handling === 97 | exit = tests(exiIn, exiOut, s, sd); 98 | if (exit == TEST_RESPOND) { 99 | session_unlock(s); 100 | session_remove_ref(s); 101 | return 0; 102 | } else if (exit == TEST_CLOSE_CONNECTION) { 103 | session_unlock(s); 104 | session_remove_ref(s); 105 | return -1; 106 | } 107 | if (exiIn->V2G_Message.Body.SessionSetupReq_isUsed) { 108 | exiOut->V2G_Message.Body.SessionSetupRes_isUsed = 1u; 109 | init_v2gSessionSetupResType(&exiOut->V2G_Message.Body.SessionSetupRes); 110 | err = handle_session_setup(exiIn, exiOut, s, sd); 111 | } 112 | else if (exiIn->V2G_Message.Body.ServiceDiscoveryReq_isUsed) { 113 | exiOut->V2G_Message.Body.ServiceDiscoveryRes_isUsed = 1u; 114 | init_v2gServiceDiscoveryResType(&exiOut->V2G_Message.Body.ServiceDiscoveryRes); 115 | err = handle_service_discovery(exiIn, exiOut, s, sd); 116 | } else if (exiIn->V2G_Message.Body.ServiceDetailReq_isUsed) { 117 | exiOut->V2G_Message.Body.ServiceDetailRes_isUsed = 1u; 118 | init_v2gServiceDetailResType(&exiOut->V2G_Message.Body.ServiceDetailRes); 119 | err = handle_service_detail(exiIn, exiOut, s, sd); 120 | } else if (exiIn->V2G_Message.Body.PaymentServiceSelectionReq_isUsed) { 121 | exiOut->V2G_Message.Body.PaymentServiceSelectionRes_isUsed= 1u; 122 | init_v2gPaymentServiceSelectionResType(&exiOut->V2G_Message.Body.PaymentServiceSelectionRes); 123 | err = payment_service_selection(exiIn, exiOut, s, sd); 124 | } else if (exiIn->V2G_Message.Body.PaymentDetailsReq_isUsed) { 125 | exiOut->V2G_Message.Body.PaymentDetailsRes_isUsed = 1u; 126 | init_v2gPaymentDetailsResType(&exiOut->V2G_Message.Body.PaymentDetailsRes); 127 | err = handle_payment_detail(exiIn, exiOut, s, sd); 128 | } else if (exiIn->V2G_Message.Body.AuthorizationReq_isUsed) { 129 | exiOut->V2G_Message.Body.AuthorizationRes_isUsed = 1u; 130 | init_v2gAuthorizationResType(&exiOut->V2G_Message.Body.AuthorizationRes); 131 | err = handle_authorization(exiIn, exiOut, s, sd); 132 | } else if (exiIn->V2G_Message.Body.ChargeParameterDiscoveryReq_isUsed) { 133 | exiOut->V2G_Message.Body.ChargeParameterDiscoveryRes_isUsed = 1u; 134 | init_v2gChargeParameterDiscoveryResType(&exiOut->V2G_Message.Body.ChargeParameterDiscoveryRes); 135 | err = handle_charge_parameters(exiIn, exiOut, s, sd); 136 | } else if (exiIn->V2G_Message.Body.PowerDeliveryReq_isUsed) { 137 | exiOut->V2G_Message.Body.PowerDeliveryRes_isUsed = 1u; 138 | init_v2gPowerDeliveryResType(&exiOut->V2G_Message.Body.PowerDeliveryRes); 139 | err = handle_power_delivery(exiIn, exiOut, s, sd); 140 | } else if (exiIn->V2G_Message.Body.ChargingStatusReq_isUsed) { 141 | exiOut->V2G_Message.Body.ChargingStatusRes_isUsed = 1u; 142 | init_v2gChargingStatusResType(&exiOut->V2G_Message.Body.ChargingStatusRes); 143 | err = handle_charging_status(exiIn, exiOut, s, sd); 144 | } else if (exiIn->V2G_Message.Body.MeteringReceiptReq_isUsed) { 145 | err = -1; 146 | //errn = meteringReceipt(exiIn, exiOut); 147 | } else if (exiIn->V2G_Message.Body.SessionStopReq_isUsed) { 148 | exiOut->V2G_Message.Body.SessionStopRes_isUsed = 1u; 149 | init_v2gSessionStopResType(&exiOut->V2G_Message.Body.SessionStopRes); 150 | err = handle_session_stop(exiIn, exiOut, s, sd); 151 | } else { 152 | printf("create_response_message: request type not found\n"); 153 | } 154 | session_unlock(s); 155 | session_remove_ref(s); 156 | if (err != 0) { 157 | printf("Handle request returning %d\n", err); 158 | } 159 | return err; 160 | } 161 | 162 | 163 | // === Note that this test only tests TCP since no tests are defined for TLS === 164 | void evcc_tester(const char* iface) { 165 | int tcp_port; 166 | // === Bind to dynamic port === 167 | int tcp_sockfd = bind_v2gport(&tcp_port); 168 | if (tcp_sockfd < 0) { 169 | printf("secc_bind_tls returned %d\n", tcp_sockfd); 170 | return; 171 | } 172 | init_sessions(); 173 | secc_listen_tcp(tcp_sockfd, &create_response_message_evcctest); 174 | sdp_listen(iface, 0, tcp_port); 175 | printf("Test %d: SupportedAppProtocol Request\n", ++ntest); 176 | } 177 | 178 | void threadmain(int argc, 179 | char *argv[]) 180 | { 181 | const char *iface; 182 | int opt, notls = 0; 183 | argv0 = argv[0]; 184 | while ((opt = getopt(argc, argv, "vn")) != -1) { 185 | switch (opt) { 186 | /*case 's': 187 | slac++; 188 | break;*/ 189 | case 'v': 190 | chattyv2g++; 191 | break; 192 | case 'n': // no tls 193 | notls++; 194 | break; 195 | default: 196 | usage(); 197 | } 198 | } 199 | if (optind >= argc) { usage(); } 200 | iface = argv[optind]; 201 | secc_free_charge++; 202 | evcc_tester(iface); 203 | exit(0); 204 | } 205 | -------------------------------------------------------------------------------- /example/ISO15118-4_tests/evcc_tests/evcctests.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "server.h" 4 | #include 5 | 6 | enum { 7 | TEST_IGNORE = 0, 8 | TEST_RESPOND, 9 | TEST_CLOSE_CONNECTION, 10 | }; 11 | enum{ 12 | TC_EVCC_CMN_VTB_SessionSetup_001 = 1, 13 | TC_EVCC_CMN_VTB_ServiceDiscovery_001, 14 | TC_EVCC_CMN_VTB_ServiceDiscovery_002, 15 | TC_EVCC_CMN_VTB_ServiceDiscovery_003, 16 | TC_EVCC_CMN_VTB_ServiceDiscovery_004, 17 | TC_EVCC_CMN_VTB_ServiceDetailAndPaymentSelection_001, 18 | //TC_EVCC_CMN_VTB_Authorization_001, 19 | //TC_EVCC_CMN_VTB_Authorization_002, 20 | //TC_EVCC_CMN_VTB_Authorization_003, 21 | TC_EVCC_CMN_VTB_ChargeParameterDiscovery_001, 22 | TC_EVCC_AC_VTB_PowerDelivery_001, 23 | //TC_EVCC_AC_VTB_PowerDelivery_002, 24 | //TC_EVCC_AC_VTB_PowerDelivery_003, 25 | /*TC_EVCC_CMN_VTB_SessionStop_001, 26 | TC_EVCC_CMN_VTB_SessionStop_003, 27 | TC_EVCC_CMN_VTB_SessionStop_004, 28 | TC_EVCC_CMN_VTB_SessionStop_005,*/ 29 | TC_EVCC_AC_VTB_ChargingStatus_001, 30 | /*TC_EVCC_AC_VTB_ChargingStatus_002, 31 | TC_EVCC_AC_VTB_ChargingStatus_003, 32 | TC_EVCC_AC_VTB_ChargingStatus_004, 33 | TC_EVCC_AC_VTB_ChargingStatus_005,*/ 34 | TC_EVCC_LAST, 35 | } test_t; 36 | 37 | extern int succeses, ntest; 38 | 39 | int service_detail_and_payment_selection_testseq( 40 | struct v2gEXIDocument *exiIn, 41 | struct v2gEXIDocument *exiOut, 42 | session_t *s, session_data_t *sd); 43 | 44 | void service_discovery_test1(struct v2gEXIDocument *exiIn, 45 | struct v2gEXIDocument *exiOut, 46 | session_t *s, session_data_t *sd); 47 | 48 | int service_discovery_test_sequence(struct v2gEXIDocument *exiIn, 49 | struct v2gEXIDocument *exiOut, 50 | session_t *s, session_data_t *sd); 51 | int charge_parameter_discovery_test( 52 | struct v2gEXIDocument *exiIn, 53 | struct v2gEXIDocument *exiOut, 54 | session_t *s, session_data_t *sd); 55 | 56 | int power_delivery_test( 57 | struct v2gEXIDocument *exiIn, 58 | struct v2gEXIDocument *exiOut, 59 | session_t *s, session_data_t *sd); 60 | int charging_status_test( 61 | struct v2gEXIDocument *exiIn, 62 | struct v2gEXIDocument *exiOut, 63 | session_t *s, session_data_t *sd); 64 | -------------------------------------------------------------------------------- /example/ISO15118-4_tests/evcc_tests/powerdelivery_test.c: -------------------------------------------------------------------------------- 1 | #include "evcctests.h" 2 | 3 | int power_delivery_test( 4 | struct v2gEXIDocument *exiIn, 5 | struct v2gEXIDocument *exiOut, 6 | session_t *s, session_data_t *sd) 7 | 8 | { 9 | static int step = 0; 10 | int ret = TEST_IGNORE; 11 | switch (step) { 12 | case 0: 13 | if (exiIn->V2G_Message.Body.ChargeParameterDiscoveryReq_isUsed) { 14 | exiOut->V2G_Message.Body.ChargeParameterDiscoveryRes_isUsed = 1u; 15 | init_v2gChargeParameterDiscoveryResType(&exiOut->V2G_Message.Body.ChargeParameterDiscoveryRes); 16 | if (handle_charge_parameters(exiIn, exiOut, s, sd) != 0) { 17 | printf("Test %d Failed: Charge Param initial request required\n", ntest); 18 | exit(-1); 19 | } 20 | step = 1; 21 | } 22 | break; 23 | case 1: 24 | if (exiIn->V2G_Message.Body.PowerDeliveryReq_isUsed) { 25 | if (s == NULL) { 26 | printf("Test %d Failed: NO session\n", ntest); 27 | } 28 | } else { 29 | printf("Test %d Failed: Unexpected Request\n", ntest); 30 | exit(-1); 31 | } 32 | printf("Test %d Succesful: Power Delivery Request Received\n", ntest); 33 | succeses++; 34 | ret = TEST_CLOSE_CONNECTION; 35 | step = 0; 36 | ntest++; 37 | break; 38 | } 39 | return ret; 40 | } 41 | -------------------------------------------------------------------------------- /example/ISO15118-4_tests/evcc_tests/servicedetailandpayment_test.c: -------------------------------------------------------------------------------- 1 | #include "evcctests.h" 2 | 3 | int service_detail_and_payment_selection_testseq( 4 | struct v2gEXIDocument *exiIn, 5 | struct v2gEXIDocument *exiOut, 6 | session_t *s, session_data_t *sd) 7 | 8 | { 9 | static int step = 0; 10 | int ret = TEST_IGNORE; 11 | switch (step) { 12 | case 0: 13 | if (exiIn->V2G_Message.Body.ServiceDiscoveryReq_isUsed) { 14 | step = 1; 15 | } 16 | break; 17 | case 1: 18 | if (exiIn->V2G_Message.Body.ServiceDetailReq_isUsed) { 19 | if (handle_service_detail(exiIn, exiOut, s, sd) != 0) { 20 | printf("Test %d Failed: Invalid request\n", ntest); 21 | exit(-1); 22 | } 23 | } else if (exiIn->V2G_Message.Body.PaymentServiceSelectionReq_isUsed) { 24 | if (payment_service_selection(exiIn, exiOut, s, sd) != 0) { 25 | printf("Test %d Failed: Invalid request\n", ntest); 26 | exit(-1); 27 | } 28 | } else { 29 | printf("Test %d Failed: Unexpected Request\n", ntest); 30 | exit(-1); 31 | } 32 | printf("Test %d Succesful: ServiceDetailAndPaymentSelection\n", ntest); 33 | succeses++; 34 | ret = TEST_CLOSE_CONNECTION; 35 | step = 0; 36 | ntest++; 37 | break; 38 | } 39 | return ret; 40 | } 41 | -------------------------------------------------------------------------------- /example/ISO15118-4_tests/evcc_tests/servicediscovery_test.c: -------------------------------------------------------------------------------- 1 | #include "evcctests.h" 2 | 3 | void service_discovery_test1(struct v2gEXIDocument *exiIn, 4 | struct v2gEXIDocument *exiOut, 5 | session_t *s, session_data_t *sd) 6 | { 7 | struct v2gServiceDiscoveryResType *res = &exiOut->V2G_Message.Body.ServiceDiscoveryRes; 8 | if (s == NULL) { 9 | printf("Test %d failed, session not found\n", ntest); 10 | return; 11 | } 12 | res->ResponseCode = v2gresponseCodeType_OK; 13 | printf("Test %d: Service Discovery, succesful\n", ntest); 14 | succeses++; 15 | } 16 | void service_discovery_test2n3n4(struct v2gEXIDocument *exiIn, 17 | struct v2gEXIDocument *exiOut, 18 | session_t *s, session_data_t *sd) 19 | { 20 | struct v2gServiceDiscoveryResType *res = &exiOut->V2G_Message.Body.ServiceDiscoveryRes; 21 | exiOut->V2G_Message.Body.ServiceDiscoveryRes_isUsed = 1u; 22 | init_v2gServiceDiscoveryResType(&exiOut->V2G_Message.Body.ServiceDiscoveryRes); 23 | if (s == NULL) { 24 | printf("Test %d failed, session not found\n", ntest); 25 | return; 26 | } 27 | res->ChargeService.FreeService = secc_free_charge && 1; 28 | res->ChargeService.ServiceCategory = v2gserviceCategoryType_EVCharging; 29 | res->ChargeService.SupportedEnergyTransferMode.EnergyTransferMode.array[0] = 30 | v2gEnergyTransferModeType_AC_single_phase_core; 31 | res->ChargeService.SupportedEnergyTransferMode.EnergyTransferMode.array[1] = 32 | v2gEnergyTransferModeType_AC_three_phase_core; 33 | res->ChargeService.SupportedEnergyTransferMode.EnergyTransferMode.arrayLen = 2; 34 | 35 | res->PaymentOptionList.PaymentOption.array[0] = v2gpaymentOptionType_ExternalPayment; // EVSE handles the payment 36 | res->PaymentOptionList.PaymentOption.array[1] = v2gpaymentOptionType_Contract; 37 | res->PaymentOptionList.PaymentOption.arrayLen = 2; 38 | if (ntest == TC_EVCC_CMN_VTB_ServiceDiscovery_002) { 39 | res->ResponseCode = v2gresponseCodeType_FAILED; 40 | } else if (ntest == TC_EVCC_CMN_VTB_ServiceDiscovery_003) { 41 | res->ResponseCode = v2gresponseCodeType_FAILED_SequenceError; 42 | } else {//if(test == TC_EVCC_CMN_VTB_ServiceDiscovery_004){ 43 | res->ResponseCode = v2gresponseCodeType_FAILED_SignatureError; 44 | } 45 | 46 | } 47 | 48 | 49 | int service_discovery_test_sequence(struct v2gEXIDocument *exiIn, 50 | struct v2gEXIDocument *exiOut, 51 | session_t *s, session_data_t *sd) 52 | { 53 | static int step = 0; 54 | if (step == 1) { 55 | if (exiIn->V2G_Message.Body.SessionSetupReq_isUsed) { 56 | printf("Test %d Succesful: Service Discovery with Failed Code\n", ntest); 57 | succeses++; 58 | } else { 59 | printf("Test %d Failed: Service Discovery with Failed Code\n", ntest); 60 | } 61 | step = 0; 62 | ntest++; 63 | return TEST_IGNORE; 64 | } 65 | if (exiIn->V2G_Message.Body.ServiceDiscoveryReq_isUsed) { 66 | step = 1; 67 | service_discovery_test2n3n4(exiIn, exiOut, s, sd); 68 | return TEST_RESPOND; 69 | } 70 | return TEST_IGNORE; 71 | } 72 | -------------------------------------------------------------------------------- /example/ISO15118-4_tests/secc_tests/Makefile: -------------------------------------------------------------------------------- 1 | CC=clang 2 | LD=clang 3 | CFLAGS=-g -Os -Wall -pedantic 4 | LDFLAGS= 5 | 6 | TARGET=secc_test 7 | 8 | SOURCES=\ 9 | secc_test.c\ 10 | ../../client.c\ 11 | 12 | HEADERS=\ 13 | 14 | OBJECTS=\ 15 | $(SOURCES:.c=.o)\ 16 | 17 | LIBS=\ 18 | -L../..\ 19 | -lnikolav2g\ 20 | -lmultitask\ 21 | -lOpenV2G\ 22 | -lpolarssl\ 23 | -lm\ 24 | -lrt\ 25 | -lpthread\ 26 | 27 | INCLUDES=-I../.. 28 | 29 | .c.o: 30 | $(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $< 31 | 32 | $(TARGET): $(OBJECTS) 33 | $(LD) $(LDFLAGS) -o $(TARGET) $(OBJECTS) $(LIBS) 34 | 35 | clean: 36 | rm -f $(OBJECTS) $(TARGET) 37 | 38 | all: $(TARGET) 39 | -------------------------------------------------------------------------------- /example/ISO15118-4_tests/secc_tests/powerdeliveryres_test.c: -------------------------------------------------------------------------------- 1 | #define BASE_POWER_DELIVERY \ 2 | int err;\ 3 | struct v2gEXIDocument exiIn;\ 4 | struct v2gEXIDocument exiOut;\ 5 | init_v2g_request(&exiIn, s);\ 6 | exiIn.V2G_Message.Body.PowerDeliveryReq_isUsed = 1u;\ 7 | init_v2gPowerDeliveryReqType(&exiIn.V2G_Message.Body.PowerDeliveryReq);\ 8 | exiIn.V2G_Message.Body.PowerDeliveryReq.DC_EVPowerDeliveryParameter_isUsed = 0; 9 | 10 | #define POWER_DELIVERY_REQ\ 11 | err = v2g_request(conn, &exiIn, &exiOut);\ 12 | if (err != 0) {\ 13 | printf("power_delivery_request v2g_request error, exiting\n");\ 14 | return -1;\ 15 | } 16 | 17 | #define VALIDATE_POWER_DELIVERY \ 18 | if (exiOut.V2G_Message.Body.PowerDeliveryRes_isUsed != 1u) {\ 19 | printf("power_delivery_request: wrong response type\n");\ 20 | return -1;\ 21 | }\ 22 | if (verify_response_code(exiOut.V2G_Message.Body.PowerDeliveryRes.ResponseCode) != 0) {\ 23 | printf("power_delivery_request: response NOT ok, code = %d\n", exiOut.V2G_Message.Body.PowerDeliveryRes.ResponseCode);\ 24 | return -1;\ 25 | } 26 | 27 | int test_power_delivery_request1(evcc_conn_t *conn, ev_session_t *s) 28 | { 29 | BASE_POWER_DELIVERY 30 | exiIn.V2G_Message.Body.PowerDeliveryReq.ChargeProgress = v2gchargeProgressType_Start; 31 | // === A charging profile is used for this request=== 32 | exiIn.V2G_Message.Body.PowerDeliveryReq.ChargingProfile_isUsed = 0; 33 | POWER_DELIVERY_REQ 34 | VALIDATE_POWER_DELIVERY 35 | return 0; 36 | } 37 | 38 | int test_power_delivery_request2(evcc_conn_t *conn, ev_session_t *s) 39 | { 40 | BASE_POWER_DELIVERY 41 | // === A charging profile is used for this request=== 42 | exiIn.V2G_Message.Body.PowerDeliveryReq.ChargingProfile_isUsed = 0; 43 | exiIn.V2G_Message.Body.PowerDeliveryReq.ChargeProgress = v2gchargeProgressType_Renegotiate; 44 | POWER_DELIVERY_REQ 45 | VALIDATE_POWER_DELIVERY 46 | return 0; 47 | } 48 | 49 | int test_power_delivery_request3(evcc_conn_t *conn, ev_session_t *s) 50 | { 51 | BASE_POWER_DELIVERY 52 | // === A charging profile is used for this request=== 53 | exiIn.V2G_Message.Body.PowerDeliveryReq.ChargingProfile_isUsed = 0; 54 | exiIn.V2G_Message.Body.PowerDeliveryReq.ChargeProgress = v2gchargeProgressType_Stop; 55 | POWER_DELIVERY_REQ 56 | VALIDATE_POWER_DELIVERY 57 | return 0; 58 | } 59 | 60 | int test_power_delivery_request4(evcc_conn_t *conn, ev_session_t *s) 61 | { 62 | BASE_POWER_DELIVERY 63 | struct v2gChargingProfileType *profile = &exiIn.V2G_Message.Body.PowerDeliveryReq.ChargingProfile; 64 | exiIn.V2G_Message.Body.PowerDeliveryReq.SAScheduleTupleID = s->pmax_schedule.tupleid; 65 | exiIn.V2G_Message.Body.PowerDeliveryReq.ChargeProgress = v2gchargeProgressType_Start; 66 | // === A charging profile is used for this request=== 67 | exiIn.V2G_Message.Body.PowerDeliveryReq.ChargingProfile_isUsed = 1u; 68 | 69 | profile->ProfileEntry.arrayLen = 0; 70 | unsigned int power = 10000; 71 | unsigned int mpower = 2 * power; 72 | SetProfileEntry(profile, 1400, power, 1); 73 | SetProfileEntry(profile, 1410, power, 2); 74 | SetProfileEntry(profile, 1420, power, 3); 75 | SetProfileEntry(profile, 1430, mpower, 2); 76 | SetProfileEntry(profile, 1440, power, 1); 77 | SetProfileEntry(profile, 1450, power, 2); 78 | SetProfileEntry(profile, 1500, power, 3); 79 | POWER_DELIVERY_REQ 80 | if (exiOut.V2G_Message.Body.PowerDeliveryRes_isUsed != 1u) { 81 | printf("power_delivery_request: wrong response type\n"); 82 | return -1;\ 83 | }\ 84 | if (exiOut.V2G_Message.Body.PowerDeliveryRes.ResponseCode 85 | != v2gresponseCodeType_FAILED_ChargingProfileInvalid) { 86 | printf("power_delivery_request: unexpected response code, code = %d, \ 87 | expected v2gresponseCodeType_FAILED_ChargingProfileInvalid\n", exiOut.V2G_Message.Body.PowerDeliveryRes.ResponseCode); 88 | return -1; 89 | } 90 | return 0; 91 | } 92 | 93 | int test_power_delivery_request5(evcc_conn_t *conn, ev_session_t *s) 94 | { 95 | BASE_POWER_DELIVERY 96 | struct v2gChargingProfileType *profile = &exiIn.V2G_Message.Body.PowerDeliveryReq.ChargingProfile; 97 | exiIn.V2G_Message.Body.PowerDeliveryReq.SAScheduleTupleID = s->pmax_schedule.tupleid; 98 | exiIn.V2G_Message.Body.PowerDeliveryReq.ChargeProgress = v2gchargeProgressType_Start; 99 | // === A charging profile is used for this request=== 100 | exiIn.V2G_Message.Body.PowerDeliveryReq.ChargingProfile_isUsed = 1u; 101 | 102 | profile->ProfileEntry.arrayLen = 0; 103 | unsigned int power = 10000; 104 | unsigned int mpower = power / 2; 105 | SetProfileEntry(profile, 1400, mpower, 1); 106 | SetProfileEntry(profile, 1410, mpower, 2); 107 | SetProfileEntry(profile, 1420, mpower, 3); 108 | SetProfileEntry(profile, 1430, mpower, 2); 109 | SetProfileEntry(profile, 1440, mpower, 1); 110 | SetProfileEntry(profile, 1450, mpower, 2); 111 | SetProfileEntry(profile, 1500, mpower, 3); 112 | POWER_DELIVERY_REQ 113 | VALIDATE_POWER_DELIVERY 114 | return 0; 115 | } 116 | -------------------------------------------------------------------------------- /example/ISO15118-4_tests/secc_tests/secc_test: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cblach/nikola-v2gstack/5609b011fd8a4edfa27ee9afabcd7251dc4bcece/example/ISO15118-4_tests/secc_tests/secc_test -------------------------------------------------------------------------------- /example/ISO15118-4_tests/secc_tests/secc_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "client.h" 4 | #include 5 | #include "powerdeliveryres_test.c" 6 | int session_request(evcc_conn_t *conn, ev_session_t *s); 7 | int service_discovery_request(evcc_conn_t *conn, ev_session_t *s); 8 | 9 | 10 | static const char *argv0; 11 | int succeses = 0, n = 0; 12 | 13 | bool USE_TLS = true; 14 | 15 | void usage(void) 16 | { 17 | fprintf(stderr, "Usage: %s [-sv] [--] interface node-type\n", argv0); 18 | exit(1); 19 | } 20 | 21 | void test_validate_port(struct sockaddr_in6 *addr) 22 | { 23 | if (addr->sin6_port == 0) { 24 | printf("Test Failed\n"); 25 | } else { 26 | printf("Success: SDP found TCP on port %u\n", addr->sin6_port); 27 | succeses++; 28 | } 29 | } 30 | 31 | 32 | void secc_tester(const char* iface) { 33 | evcc_conn_t conn; 34 | ev_session_t s; 35 | struct sockaddr_in6 secc_tlsaddr, secc_tcpaddr; 36 | int err; 37 | memset(&conn, 0, sizeof(conn)); 38 | memset(&s, 0, sizeof(s)); 39 | 40 | err = load_contract("../../certs/contractchain.pem", "../../certs/contract.key", &s); 41 | if (err != 0) { 42 | printf("ev_example: load_contract error\n"); 43 | return; 44 | } 45 | printf("Test %d: SDP with TLS enabled (Security = 0x00)... ", ++n); 46 | err = ev_sdp_discover_evse(iface, &secc_tlsaddr, true); 47 | if (err != 0) { 48 | printf("Test Failed\n"); 49 | } else { 50 | printf("Success: SDP found TLS on port %u\n", secc_tlsaddr.sin6_port); 51 | succeses++; 52 | } 53 | printf("Test %d: SDP with TLS disabled (Security = 0x10... ", ++n); 54 | err = ev_sdp_discover_evse(iface, &secc_tcpaddr, false); 55 | if (err != 0) { 56 | printf("Test Failed\n"); 57 | } else { 58 | test_validate_port(&secc_tcpaddr); 59 | } 60 | printf("Test %d: TLS serving & 15118 Protocol Handshake\n", ++n); 61 | if (USE_TLS && secc_tlsaddr.sin6_port != 0) { 62 | memcpy(&conn.addr, &secc_tlsaddr, sizeof(conn.addr)); 63 | err = evcc_connect_tls(&conn, "../../certs/ev.pem", "../../certs/ev.key"); 64 | } else if (!USE_TLS && secc_tlsaddr.sin6_port != 0) { 65 | memcpy(&conn.addr, &secc_tcpaddr, sizeof(conn.addr)); 66 | err = evcc_connect_tcp(&conn); 67 | } else { 68 | printf("Unable to proceed as SDP found no viable port for chosen security\n"); 69 | return; 70 | } 71 | if (err != 0) { 72 | printf("Connect failed, unable to proceed\n"); 73 | return; 74 | } 75 | succeses++; 76 | printf("Test %d: Session Response\n", ++n); 77 | err = session_request(&conn, &s); 78 | if (err != 0 || s.id == 0) { 79 | printf("Test Failed: Session Setup. Unable to proceed.\n"); 80 | return; 81 | } 82 | succeses++; 83 | printf("Test %d: Service Discovery Response\n", ++n); 84 | err = service_discovery_request(&conn, &s); 85 | if (err != 0) { 86 | printf("Test Failed: Service Discovery to proceed.\n"); 87 | return; 88 | } 89 | succeses++; 90 | err = payment_selection_request(&conn, &s); 91 | if (err != 0) { 92 | printf("ev_example: payment_selection_request err\n"); 93 | return; 94 | } 95 | err = payment_details_request(&conn, &s); 96 | if (err != 0) { 97 | printf("ev_example: payment_details_request err\n"); 98 | return; 99 | } 100 | err = authorization_request(&conn, &s); 101 | if (err != 0) { 102 | printf("ev_example: authorization_request err\n"); 103 | return; 104 | } 105 | err = charge_parameter_request(&conn, &s); 106 | if (err != 0) { 107 | printf("ev_example: charge_parameter_request err\n"); 108 | return; 109 | } 110 | printf("Test %d: Power Delivery Response\n", ++n); 111 | err = test_power_delivery_request1(&conn, &s); 112 | if (err != 0) { 113 | printf("Test Failed: test_power_delivery_request1.\n"); 114 | return; 115 | } 116 | succeses++; 117 | printf("Test %d: Power Delivery Response\n", ++n); 118 | err = test_power_delivery_request2(&conn, &s); 119 | if (err != 0) { 120 | printf("Test Failed: test_power_delivery_request2.\n"); 121 | return; 122 | } 123 | succeses++; 124 | printf("Test %d: Power Delivery Response\n", ++n); 125 | err = test_power_delivery_request3(&conn, &s); 126 | if (err != 0) { 127 | printf("Test Failed: test_power_delivery_request3.\n"); 128 | return; 129 | } 130 | succeses++; 131 | printf("Test %d: Power Delivery Response\n", ++n); 132 | err = test_power_delivery_request4(&conn, &s); 133 | if (err != 0) { 134 | printf("Test Failed: test_power_delivery_request4.\n"); 135 | return; 136 | } 137 | succeses++; 138 | printf("Test %d: Power Delivery Response\n", ++n); 139 | err = test_power_delivery_request5(&conn, &s); 140 | if (err != 0) { 141 | printf("Test Failed: test_power_delivery_request5.\n"); 142 | return; 143 | } 144 | succeses++; 145 | } 146 | 147 | 148 | void threadmain(int argc, 149 | char *argv[]) 150 | { 151 | const char *iface; 152 | int opt, notls = 0; 153 | 154 | argv0 = argv[0]; 155 | while ((opt = getopt(argc, argv, "vn")) != -1) { 156 | switch (opt) { 157 | /*case 's': 158 | slac++; 159 | break;*/ 160 | case 'v': 161 | chattyv2g++; 162 | break; 163 | case 'n': // no tls 164 | notls++; 165 | break; 166 | default: 167 | usage(); 168 | } 169 | } 170 | if (optind >= argc) { usage(); } 171 | iface = argv[optind]; 172 | printf("LLOLOLLOOOLLOLOLOOLOLOLOLOL %s\n", iface); 173 | secc_tester(iface); 174 | printf("Done testing, %d of %d tests were succesful\n", succeses, n); 175 | exit(0); 176 | } 177 | -------------------------------------------------------------------------------- /example/Makefile: -------------------------------------------------------------------------------- 1 | CC=clang 2 | LD=clang 3 | CFLAGS=-g -Os -Wall -pedantic 4 | LDFLAGS= 5 | 6 | TARGET=example.out 7 | 8 | SOURCES=$(wildcard *.c slac/*.c) 9 | OBJECTS=$(SOURCES:.c=.o) 10 | 11 | LIBS=\ 12 | -L..\ 13 | -lnikolav2g\ 14 | -lmultitask\ 15 | -lOpenV2G\ 16 | -lpolarssl\ 17 | -lm\ 18 | -lrt\ 19 | -lpthread\ 20 | 21 | INCLUDES=-I.. 22 | 23 | all: $(TARGET) 24 | 25 | .c.o: 26 | $(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $< 27 | 28 | $(TARGET): $(OBJECTS) 29 | $(LD) $(LDFLAGS) -o $(TARGET) $(OBJECTS) $(LIBS) 30 | 31 | clean: 32 | rm -f $(OBJECTS) $(TARGET) 33 | 34 | .PHONY: all 35 | -------------------------------------------------------------------------------- /example/bash/connect: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ssh -i ~/.ssh/cb_nikola \ 3 | -M -S ~/.ssh/controlmasters/debian@192.168.7.2:22 debian@192.168.7.2 4 | -------------------------------------------------------------------------------- /example/bash/deploy: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | dstdir=/home/debian/Code/ 3 | 4 | echo Synchronizing; 5 | rsync -qr --delete -e\ 6 | "ssh -i ~/.ssh/cb_nikola" ../../ debian@192.168.7.2:$dstdir \ 7 | --exclude-from rsync-exclude.txt && \ 8 | echo Building && \ 9 | ssh -i ~/.ssh/cb_nikola -S ~/.ssh/controlmasters/debian@192.168.7.2:22 \ 10 | debian@192.168.7.2 \ 11 | "cd ${dstdir} && make && cd example && make" 12 | -------------------------------------------------------------------------------- /example/bash/rsync-exclude.txt: -------------------------------------------------------------------------------- 1 | *.o 2 | **.a 3 | -------------------------------------------------------------------------------- /example/certs/.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCrUfUuLx/9HyOW 3 | 2WNfxzvu6VuwBo5r5NPZKxZHe9xAPKy9XNPS1Z9LFqJWvkj3nS1w7kalf8QcUO2t 4 | C4b68BBdq5VrZ1ZNCK4GCwgtJ5rq5yW7pQJLzjvPSmBshoUKFeFtxsJP7HlfndpX 5 | jlcMfUGzZRspSYeygbkslZthIkuwIxbiKD4h1y1XzZLh95S0Ev+6N2/yh0ty9VuQ 6 | UjqP3dxRHMHdr5yakSb4Ls/gxnj/5PW8JSY00j1/HIj7vGiybrhCumnPXOnBeHuc 7 | DTmdIZlLlOBoe+G1e8fmx43n6yaaQDNa2y/zt5AZZoHlIIrBh04/DR90p0KidEW/ 8 | BO8aNnmph34cCxF4O6/N9FSfMP2qi2XIBpCB+dEgeSuXC9wITRMVMShsDh7kNNUE 9 | kCVklSeM38NX7K2awKEtJgIPJ8e+Ysd5D7/+Y4/hE9ISGGsKdtFh877zSYXqhG69 10 | xOIpOeQTVWO+P/rDRGfl71Ah1Z77C9Sj22c25gVeDl9AVGqDIGlRT5fTihvFVL47 11 | bCAWqvGAZbA2lYUNwur24jd/lRlKEsYmhu0f+Yav7wVIhf4VWoJMlogoOGTgSkoZ 12 | VUHT7QSvtiqTjf+doIDUKgyK8TO4lJ7OqwN8qi8irvkQ+9S1HcZPVgXCYEiwwKs8 13 | Obcz3J7ex2z91B3m7wMvpv2jvqSVWQIDAQABAoICACpdM/HduV1Qa+6LhyVSESH9 14 | Fq0qkeFgTnqvSu0WVdqUpmqVH9RM+v0IddBZcZW5ilkYsuON+qK96GgYJ6K55nIj 15 | UemjPhYChXX9GGKCHq7jxdLm9PBl6ShTThDIGvHpgzswITC+Wy0nzaxFCIWQG8UO 16 | xW6qPiIILhA2VpvobSr7Ild+eA7KNO43aZDzNhRpdk9IEii5CBE/VwsTKmQVCrwM 17 | DmyRVnpqVmacvM7qhVVR+uoxjYcWThq2pbEKYCFve6UUj8G1SiCLeJWYN50Bh/TG 18 | tc9ggunr1/zbb93fqkYOhiR7exobVEjTrl9Icsd0fAbfad3g1ntxoSWhRLcIrYC0 19 | 6huSSe6qs1cFjVMLbbIavxzmVc74qHswyuCej64aieF4w+Z9jYlR6K2Moaf/6+Uz 20 | t9WnqaYe/gvNbG5Jk34ETLix2L4TM6z3Lim1Zrp7GUHOB7FB6BKtkG13Z6YkEyDM 21 | bDghyy7xPjdNU76DCcI9udLE9uCo97Um7NC0SwWSw9G76D8GJpmI/kJjY2pgF9wp 22 | cngGJE8NL6CO6SkRDZng/75cUdyFXoy8wDq24YHhFvJcvXFKymXFfQcN6I9H7NfB 23 | nPVMLVvI42OXi7DSo0bOAyAKkgVAtlH/GeuU9EFTw4jue5foGvSN/YtyZhshkjPx 24 | 1oYshMPU3/HwPlsCX7bZAoIBAQDTvEEJV6DtE9LKN94u4II5Z41/WhIRzllStsdn 25 | 3x+JwtQcTlsRcgIVX+4TCVQZaLQKZIa362IqfL+V7w2v4ZOrtNsLHuLII9whwPpQ 26 | oDLMLD6Sa5rFMYTlDEDq458xNpFwqxdr/o0W4hYRGVjx4ISmc29r5MgknT8pU72s 27 | VPf8BQJ6U0YYQscypqNpR0AgLAEfu6MwioKjUETYrXRZ8xAdix0+r4WdTRKq/vhu 28 | NoRJSLsIEc02hq4jSBQxlIH36V69qyopyUnHPqRwYUVUQzTt9Ftm1+8T/D5kOKj4 29 | xwSjIcgO4Gu/JbAx3dOG8913WKlPN1X1x7cjcfhGI+O6/mYnAoIBAQDPIr6I8+0I 30 | gPko+DDuY1VdgWCzlwdTR05ZYi4tKTlalQhUbWW6jN2VDCTHnninKADOFmpYsPFl 31 | lLKiTMNC24a/4CWro9uRiYZWg/OtHun2OGFIV9/2pQN/7pZhq2ywKkFbBZqiA/jR 32 | KEmSiBu2+NuxK73CShOERJuletnOCC6Dn5m+teTzo3qNw+GpF/8lq12TAFY4ZTcH 33 | vZwqjwEeKvNegVdhMh8QYmZF35aHPKu4+M+kZCAtUEUsXboHBy8iUI5RS4VtpfJx 34 | XIcQ2Jo27GJlBCSCWDqMUR2EOJOAg0ZjgLjpHbfg0KjnOvOdWZIcpuROTz7+e9ce 35 | YGxrnoZIKdh/AoIBAQDRPpOPJfg1EogXq4p5DQnkrP9awaplymTfpx38+rVyMQXp 36 | S04YOXDxqrHeQjPIQH7cXmf8TjPfBRVYmLwP+n8zCzIFFWrMmxOqj8lP8GEDAR59 37 | GLYE9mD88lUdTAFAY0rr60jkQtUvrhJcp44MyvKMhDuyho98D6Iht04UhovjlRfT 38 | x4PX6fZtidyn5+/pW1NpBsfI6rkbp/HBp/j6A4kDiT1He7Ixt78hGYRtdpbYzXV4 39 | Dg+dOQ/SciLytG6Ujx4/lm2+idvNeYyMOrSHBzvBhODnC7KLL5aA+LWT1SoyBOpu 40 | OT6B0j8DqNKn5UwR6562WFss30ePUcwnlMQzQcG1AoIBAGWejgtyYB+D5rkKyjap 41 | 1P+CsYZp2dYpbW68lBrj1pdx9hw1b2sOIMc2pfSo8lhPb9WreZD5ffRv9xNYxZLP 42 | UFRiCxkh0l6cMaPQKIQOXonFLzTmp6HBYu4ohu5zw2bJ1DZrkWlauX8jXLTewXoK 43 | rP1GkFn5LYn+fr24f5JswSUh77N4WVKOKVnTd6oVc79vSwELcUFJpq3NgAgBEHNU 44 | bwE2Q0AT7tmC7Nnqz1aZlUWyC2yDEqYoc0dI1DMOezlCfA+2FUet2jtzQXAqU74O 45 | Sld0qNSppE+im/B6MSMjRC0cwZrcUZGhzLtkWJAhmWUlG2yvxB/lcawdchN6Oja9 46 | ENECggEBAMv9RtM5YYP+V0IiP1okJpgz7OTY3I1MXgMuv+nhbZsuUQN19/dmnSvM 47 | awYbJrn6Muk8k4J34O58ENfvhkKAtEYWhx3EGXhN4l6nLe83ja5vUO0/+EDuhjvZ 48 | pWi+gG0rWjVp99Xjk7RlRlNBz3avswNM4Z+1N2uqKJQkUYpJxb3fAafkIn3OUfdd 49 | aWTuq2jaRIqfsMUEnalKXQd4mFjpwpEC7CeJXx9sb0/H7927LvP572P+4d7UikaV 50 | iZ2jPGtuvntMp3FAaCFxtYkcfT+PnwT3bHBlKM51C9uXOdgDRHGmqbUmBMymTLm+ 51 | i/WxCnPM0Dl+3tO+zAnWl4n6JgX4pRE= 52 | -----END PRIVATE KEY----- 53 | -------------------------------------------------------------------------------- /example/certs/01.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIByzCCAXGgAwIBAgIBATAKBggqhkjOPQQDAjBXMQswCQYDVQQGEwJESzERMA8G 3 | A1UEBwwIUm9za2lsZGUxFDASBgNVBAoMC0RUVSBSaXPDg8K4MR8wHQYDVQQDDBZN 4 | b2JpbGl0eSBvcGVyYXRvciByb290MCAXDTE1MDQwNDA5MjIzOFoYDzIyODkwMTE2 5 | MDkyMjM4WjA2MQswCQYDVQQGEwJESzEUMBIGA1UECgwLRFRVIFJpc8ODwrgxETAP 6 | BgNVBAMMCGNvbnRyYWN0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEz9Q7HgXy 7 | LiCtkNi2w9cXxIWvrOdKiYwi7PZPpPY54j8Qiwvg2doaToc47lhLvMj2tAra/I7w 8 | VWYQHOI2IrX8Q6NNMEswCQYDVR0TBAIwADAdBgNVHQ4EFgQUE6g8l2S2UgQEnwwn 9 | 7lKio6ZoIqMwHwYDVR0jBBgwFoAUX4X5Evu1gwSG4EWFgb3hHgeKqcIwCgYIKoZI 10 | zj0EAwIDSAAwRQIhALDRfW+4zqHNRyVkDoL9xVrgUZzoiBaGDR/+BHMQ8xJ2AiAa 11 | K09ccnj1101Up88lDrhnPdcp4PNyVyJ1Em28ettdBQ== 12 | -----END CERTIFICATE----- 13 | -------------------------------------------------------------------------------- /example/certs/02.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIByjCCAXCgAwIBAgIBAjAKBggqhkjOPQQDAjBaMQswCQYDVQQGEwJESzETMBEG 3 | A1UECAwKU29tZS1TdGF0ZTERMA8GA1UEBwwIUm9za2lsZGUxEDAOBgNVBAoMB1Jp 4 | c8ODwrgxETAPBgNVBAMMCFYyRyByb290MCAXDTE1MDUxMDE2NTY0M1oYDzIyODkw 5 | MjIxMTY1NjQzWjAyMQswCQYDVQQGEwJESzEUMBIGA1UECgwLRFRVIFJpc8ODwrgx 6 | DTALBgNVBAMMBGV2c2UwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASFblncB5rZ 7 | 9GX+X+yiYoiwhblZEQnn8q/S6QiP/woRExvlJyGo2kAl38utVo7PFOIklx/ntQ7l 8 | GSNEKwqOnJuUo00wSzAJBgNVHRMEAjAAMB0GA1UdDgQWBBT6fTlU2p85zin8lplu 9 | 2VP1iCZqCjAfBgNVHSMEGDAWgBTx81W84Vsv69BTxCTuwH+xtpz/ozAKBggqhkjO 10 | PQQDAgNIADBFAiBpNvWUK9nBo/GFXFehRt0jWEInp+UWBtOmGxby/l505QIhAOOl 11 | Ctt58sPyq+Db1ofxVAhnwiv21Cc/yEXtV+58tnSd 12 | -----END CERTIFICATE----- 13 | -------------------------------------------------------------------------------- /example/certs/03.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBxzCCAW6gAwIBAgIBAzAKBggqhkjOPQQDAjBaMQswCQYDVQQGEwJESzETMBEG 3 | A1UECAwKU29tZS1TdGF0ZTERMA8GA1UEBwwIUm9za2lsZGUxEDAOBgNVBAoMB1Jp 4 | c8ODwrgxETAPBgNVBAMMCE9FTSByb290MCAXDTE1MDUxMDE4MDQ0MloYDzIyODkw 5 | MjIxMTgwNDQyWjAwMQswCQYDVQQGEwJESzEUMBIGA1UECgwLRFRVIFJpc8ODwrgx 6 | CzAJBgNVBAMMAmV2MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEVo1gCcQyRBY3 7 | wz4w7YLfusxRyOC2P+ua5QSymo6KIGeRctB4pmPL2M3L9BINxUiOCrUtKIwrTFMS 8 | HUvGrPcb7aNNMEswCQYDVR0TBAIwADAdBgNVHQ4EFgQUN8my+tmnMXeD7rZARmmg 9 | TQ0ElQwwHwYDVR0jBBgwFoAUn9zTiVebQ38JNQOqN1C8KOr0w0QwCgYIKoZIzj0E 10 | AwIDRwAwRAIgRJA6bTHM1lmS8qX3Nxhi5dV/BFY8HStRbnrVLmMc3koCIFIQUcnO 11 | SQKwHA0C2uwooHPutTr+osBCiaOppYGZjaFc 12 | -----END CERTIFICATE----- 13 | -------------------------------------------------------------------------------- /example/certs/ca/mobilityop/index.txt: -------------------------------------------------------------------------------- 1 | V 22890116092238Z 01 unknown /C=DK/O=DTU Ris\xC3\x83\xC2\xB8/CN=contract 2 | V 22890221165643Z 02 unknown /C=DK/O=DTU Ris\xC3\x83\xC2\xB8/CN=evse 3 | V 22890221180442Z 03 unknown /C=DK/O=DTU Ris\xC3\x83\xC2\xB8/CN=ev 4 | -------------------------------------------------------------------------------- /example/certs/ca/mobilityop/index.txt.attr: -------------------------------------------------------------------------------- 1 | unique_subject = yes 2 | -------------------------------------------------------------------------------- /example/certs/ca/mobilityop/index.txt.attr.old: -------------------------------------------------------------------------------- 1 | unique_subject = yes 2 | -------------------------------------------------------------------------------- /example/certs/ca/mobilityop/index.txt.old: -------------------------------------------------------------------------------- 1 | V 22890116092238Z 01 unknown /C=DK/O=DTU Ris\xC3\x83\xC2\xB8/CN=contract 2 | V 22890221165643Z 02 unknown /C=DK/O=DTU Ris\xC3\x83\xC2\xB8/CN=evse 3 | -------------------------------------------------------------------------------- /example/certs/ca/mobilityop/openssl.cfg: -------------------------------------------------------------------------------- 1 | [ ca ] 2 | default_ca = CA_default 3 | [ CA_default ] 4 | dir = ca/mobilityop 5 | certs = $dir/certsdb 6 | new_certs_dir = $certs 7 | database = $dir/index.txt 8 | serial = $dir/serial 9 | certificate = root/mobilityop/certs/mobilityop.pem 10 | private_key = root/mobilityop/keys/mobilityop.key 11 | default_md = sha256 12 | crldir = $dir/crl 13 | crlnumber = $dir/crlnumber 14 | crl = $crldir/crl.pem 15 | RANDFILE = $dir/private/.rand 16 | x509_extensions = usr_cert 17 | copy_extensions = copy 18 | name_opt = ca_default 19 | cert_opt = ca_default 20 | default_days = 99999 21 | default_crl_days= 30 22 | preserve = no 23 | 24 | policy = policy_match 25 | 26 | [ policy_match ] 27 | countryName = match # Must be the same as the CA 28 | stateOrProvinceName = optional # Must be the same as the CA 29 | organizationName = match # Must be the same as the CA 30 | organizationalUnitName = optional # not required 31 | commonName = supplied # must be there, whatever it is 32 | emailAddress = optional # not required 33 | 34 | [ usr_cert ] 35 | basicConstraints = CA:false 36 | subjectKeyIdentifier = hash 37 | authorityKeyIdentifier = keyid,issuer 38 | -------------------------------------------------------------------------------- /example/certs/ca/mobilityop/serial: -------------------------------------------------------------------------------- 1 | 04 2 | -------------------------------------------------------------------------------- /example/certs/ca/mobilityop/serial.old: -------------------------------------------------------------------------------- 1 | 03 2 | -------------------------------------------------------------------------------- /example/certs/ca/oem/index.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cblach/nikola-v2gstack/5609b011fd8a4edfa27ee9afabcd7251dc4bcece/example/certs/ca/oem/index.txt -------------------------------------------------------------------------------- /example/certs/ca/oem/index.txt.attr: -------------------------------------------------------------------------------- 1 | unique_subject = yes 2 | -------------------------------------------------------------------------------- /example/certs/ca/oem/openssl.cfg: -------------------------------------------------------------------------------- 1 | [ ca ] 2 | default_ca = CA_default 3 | [ CA_default ] 4 | dir = ca/mobilityop 5 | certs = $dir/certsdb 6 | new_certs_dir = $certs 7 | database = $dir/index.txt 8 | serial = $dir/serial 9 | certificate = root/oem/certs/oem.pem 10 | private_key = root/oem/keys/oem.key 11 | default_md = sha256 12 | crldir = $dir/crl 13 | crlnumber = $dir/crlnumber 14 | crl = $crldir/crl.pem 15 | RANDFILE = $dir/private/.rand 16 | x509_extensions = usr_cert 17 | copy_extensions = copy 18 | name_opt = ca_default 19 | cert_opt = ca_default 20 | default_days = 99999 21 | default_crl_days= 30 22 | preserve = no 23 | 24 | policy = policy_match 25 | 26 | [ policy_match ] 27 | countryName = match # Must be the same as the CA 28 | stateOrProvinceName = optional # Must be the same as the CA 29 | organizationName = optional # Must be the same as the CA 30 | organizationalUnitName = optional # not required 31 | commonName = supplied # must be there, whatever it is 32 | emailAddress = optional # not required 33 | 34 | [ usr_cert ] 35 | basicConstraints = CA:false 36 | subjectKeyIdentifier = hash 37 | authorityKeyIdentifier = keyid,issuer 38 | -------------------------------------------------------------------------------- /example/certs/ca/oem/serial: -------------------------------------------------------------------------------- 1 | 00 2 | -------------------------------------------------------------------------------- /example/certs/ca/v2g/index.txt: -------------------------------------------------------------------------------- 1 | V 22890116092238Z 01 unknown /C=DK/O=DTU Ris\xC3\x83\xC2\xB8/CN=contract 2 | -------------------------------------------------------------------------------- /example/certs/ca/v2g/index.txt.attr: -------------------------------------------------------------------------------- 1 | unique_subject = yes 2 | -------------------------------------------------------------------------------- /example/certs/ca/v2g/openssl.cfg: -------------------------------------------------------------------------------- 1 | [ ca ] 2 | default_ca = CA_default 3 | [ CA_default ] 4 | dir = ca/mobilityop 5 | certs = $dir/certsdb 6 | new_certs_dir = $certs 7 | database = $dir/index.txt 8 | serial = $dir/serial 9 | certificate = root/v2g/certs/v2g.pem 10 | private_key = root/v2g/keys/v2g.key 11 | default_md = sha256 12 | crldir = $dir/crl 13 | crlnumber = $dir/crlnumber 14 | crl = $crldir/crl.pem 15 | RANDFILE = $dir/private/.rand 16 | x509_extensions = usr_cert 17 | copy_extensions = copy 18 | name_opt = ca_default 19 | cert_opt = ca_default 20 | default_days = 99999 21 | default_crl_days= 30 22 | preserve = no 23 | 24 | policy = policy_match 25 | 26 | [ policy_match ] 27 | countryName = match # Must be the same as the CA 28 | stateOrProvinceName = optional # Must be the same as the CA 29 | organizationName = optional # Must be the same as the CA 30 | organizationalUnitName = optional # not required 31 | commonName = supplied # must be there, whatever it is 32 | emailAddress = optional # not required 33 | 34 | [ usr_cert ] 35 | basicConstraints = CA:false 36 | subjectKeyIdentifier = hash 37 | authorityKeyIdentifier = keyid,issuer 38 | -------------------------------------------------------------------------------- /example/certs/ca/v2g/serial: -------------------------------------------------------------------------------- 1 | 00 2 | -------------------------------------------------------------------------------- /example/certs/contract.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIIBBTCBqwIBADBJMQswCQYDVQQGEwJESzERMA8GA1UEBwwIUm9za2lsZGUxFDAS 3 | BgNVBAoMC0RUVSBSaXPDg8K4MREwDwYDVQQDDAhjb250cmFjdDBZMBMGByqGSM49 4 | AgEGCCqGSM49AwEHA0IABM/UOx4F8i4grZDYtsPXF8SFr6znSomMIuz2T6T2OeI/ 5 | EIsL4NnaGk6HOO5YS7zI9rQK2vyO8FVmEBziNiK1/EOgADAKBggqhkjOPQQDAgNJ 6 | ADBGAiEAgfyjoudsFXbLvT7e0+52zxSFhmZDKwDIQl+yYSIYIl4CIQDlWbn9/A2C 7 | KVPFhO7uFmlNWrZ7iHJY9uZ7UxMoig7nhg== 8 | -----END CERTIFICATE REQUEST----- 9 | -------------------------------------------------------------------------------- /example/certs/contract.key: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PARAMETERS----- 2 | BggqhkjOPQMBBw== 3 | -----END EC PARAMETERS----- 4 | -----BEGIN EC PRIVATE KEY----- 5 | MHcCAQEEIPTCLp5lJEsUiT/Al5TcIHeKHrfzMLWpcYRfb5MTl/+FoAoGCCqGSM49 6 | AwEHoUQDQgAEz9Q7HgXyLiCtkNi2w9cXxIWvrOdKiYwi7PZPpPY54j8Qiwvg2doa 7 | Toc47lhLvMj2tAra/I7wVWYQHOI2IrX8Qw== 8 | -----END EC PRIVATE KEY----- 9 | -------------------------------------------------------------------------------- /example/certs/contract.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIByzCCAXGgAwIBAgIBATAKBggqhkjOPQQDAjBXMQswCQYDVQQGEwJESzERMA8G 3 | A1UEBwwIUm9za2lsZGUxFDASBgNVBAoMC0RUVSBSaXPDg8K4MR8wHQYDVQQDDBZN 4 | b2JpbGl0eSBvcGVyYXRvciByb290MCAXDTE1MDQwNDA5MjIzOFoYDzIyODkwMTE2 5 | MDkyMjM4WjA2MQswCQYDVQQGEwJESzEUMBIGA1UECgwLRFRVIFJpc8ODwrgxETAP 6 | BgNVBAMMCGNvbnRyYWN0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEz9Q7HgXy 7 | LiCtkNi2w9cXxIWvrOdKiYwi7PZPpPY54j8Qiwvg2doaToc47lhLvMj2tAra/I7w 8 | VWYQHOI2IrX8Q6NNMEswCQYDVR0TBAIwADAdBgNVHQ4EFgQUE6g8l2S2UgQEnwwn 9 | 7lKio6ZoIqMwHwYDVR0jBBgwFoAUX4X5Evu1gwSG4EWFgb3hHgeKqcIwCgYIKoZI 10 | zj0EAwIDSAAwRQIhALDRfW+4zqHNRyVkDoL9xVrgUZzoiBaGDR/+BHMQ8xJ2AiAa 11 | K09ccnj1101Up88lDrhnPdcp4PNyVyJ1Em28ettdBQ== 12 | -----END CERTIFICATE----- 13 | -------------------------------------------------------------------------------- /example/certs/contractchain.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIByzCCAXGgAwIBAgIBATAKBggqhkjOPQQDAjBXMQswCQYDVQQGEwJESzERMA8G 3 | A1UEBwwIUm9za2lsZGUxFDASBgNVBAoMC0RUVSBSaXPDg8K4MR8wHQYDVQQDDBZN 4 | b2JpbGl0eSBvcGVyYXRvciByb290MCAXDTE1MDQwNDA5MjIzOFoYDzIyODkwMTE2 5 | MDkyMjM4WjA2MQswCQYDVQQGEwJESzEUMBIGA1UECgwLRFRVIFJpc8ODwrgxETAP 6 | BgNVBAMMCGNvbnRyYWN0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEz9Q7HgXy 7 | LiCtkNi2w9cXxIWvrOdKiYwi7PZPpPY54j8Qiwvg2doaToc47lhLvMj2tAra/I7w 8 | VWYQHOI2IrX8Q6NNMEswCQYDVR0TBAIwADAdBgNVHQ4EFgQUE6g8l2S2UgQEnwwn 9 | 7lKio6ZoIqMwHwYDVR0jBBgwFoAUX4X5Evu1gwSG4EWFgb3hHgeKqcIwCgYIKoZI 10 | zj0EAwIDSAAwRQIhALDRfW+4zqHNRyVkDoL9xVrgUZzoiBaGDR/+BHMQ8xJ2AiAa 11 | K09ccnj1101Up88lDrhnPdcp4PNyVyJ1Em28ettdBQ== 12 | -----END CERTIFICATE----- 13 | -----BEGIN CERTIFICATE----- 14 | MIIB9zCCAZ2gAwIBAgIJAMx6i/edGHdhMAoGCCqGSM49BAMCMFcxCzAJBgNVBAYT 15 | AkRLMREwDwYDVQQHDAhSb3NraWxkZTEUMBIGA1UECgwLRFRVIFJpc8ODwrgxHzAd 16 | BgNVBAMMFk1vYmlsaXR5IG9wZXJhdG9yIHJvb3QwIBcNMTUwNDA0MDcxNzQ1WhgP 17 | NDc1MzAyMjgwNzE3NDVaMFcxCzAJBgNVBAYTAkRLMREwDwYDVQQHDAhSb3NraWxk 18 | ZTEUMBIGA1UECgwLRFRVIFJpc8ODwrgxHzAdBgNVBAMMFk1vYmlsaXR5IG9wZXJh 19 | dG9yIHJvb3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARHuL8eWXTyDtW/gdPu 20 | pTUFP083ajkOyWSmMnSfs05Zb4oJET/y7Xl2OBtoEl+RG5jm9RugPPxhJ/J5EKrM 21 | +jzdo1AwTjAdBgNVHQ4EFgQUX4X5Evu1gwSG4EWFgb3hHgeKqcIwHwYDVR0jBBgw 22 | FoAUX4X5Evu1gwSG4EWFgb3hHgeKqcIwDAYDVR0TBAUwAwEB/zAKBggqhkjOPQQD 23 | AgNIADBFAiBJBQycWHJqv8dqoqlEgvbkk4KwFlq8SpVzSdwQbKTGvAIhAOJ5IR45 24 | P3rM+nDfA919SKB1oVwGe098mWuFbMybeL7h 25 | -----END CERTIFICATE----- 26 | -------------------------------------------------------------------------------- /example/certs/ev.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIH9MIGlAgEAMEMxCzAJBgNVBAYTAkRLMREwDwYDVQQHDAhSb3NraWxkZTEUMBIG 3 | A1UECgwLRFRVIFJpc8ODwrgxCzAJBgNVBAMMAmV2MFkwEwYHKoZIzj0CAQYIKoZI 4 | zj0DAQcDQgAEVo1gCcQyRBY3wz4w7YLfusxRyOC2P+ua5QSymo6KIGeRctB4pmPL 5 | 2M3L9BINxUiOCrUtKIwrTFMSHUvGrPcb7aAAMAoGCCqGSM49BAMCA0cAMEQCIAGZ 6 | KvIgBry0zIEgsicvI3EZenYCBdApE61KaqjU7TkDAiB45kJpRUs7Eibpmv+70F4B 7 | 2SFPZeoQjyLOr05J3BNt6A== 8 | -----END CERTIFICATE REQUEST----- 9 | -------------------------------------------------------------------------------- /example/certs/ev.key: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PARAMETERS----- 2 | BggqhkjOPQMBBw== 3 | -----END EC PARAMETERS----- 4 | -----BEGIN EC PRIVATE KEY----- 5 | MHcCAQEEIKhD0u3B1KAY/WtIMP4LFL2DzV4SWK4+N2dPQslXozeloAoGCCqGSM49 6 | AwEHoUQDQgAEVo1gCcQyRBY3wz4w7YLfusxRyOC2P+ua5QSymo6KIGeRctB4pmPL 7 | 2M3L9BINxUiOCrUtKIwrTFMSHUvGrPcb7Q== 8 | -----END EC PRIVATE KEY----- 9 | -------------------------------------------------------------------------------- /example/certs/ev.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBxzCCAW6gAwIBAgIBAzAKBggqhkjOPQQDAjBaMQswCQYDVQQGEwJESzETMBEG 3 | A1UECAwKU29tZS1TdGF0ZTERMA8GA1UEBwwIUm9za2lsZGUxEDAOBgNVBAoMB1Jp 4 | c8ODwrgxETAPBgNVBAMMCE9FTSByb290MCAXDTE1MDUxMDE4MDQ0MloYDzIyODkw 5 | MjIxMTgwNDQyWjAwMQswCQYDVQQGEwJESzEUMBIGA1UECgwLRFRVIFJpc8ODwrgx 6 | CzAJBgNVBAMMAmV2MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEVo1gCcQyRBY3 7 | wz4w7YLfusxRyOC2P+ua5QSymo6KIGeRctB4pmPL2M3L9BINxUiOCrUtKIwrTFMS 8 | HUvGrPcb7aNNMEswCQYDVR0TBAIwADAdBgNVHQ4EFgQUN8my+tmnMXeD7rZARmmg 9 | TQ0ElQwwHwYDVR0jBBgwFoAUn9zTiVebQ38JNQOqN1C8KOr0w0QwCgYIKoZIzj0E 10 | AwIDRwAwRAIgRJA6bTHM1lmS8qX3Nxhi5dV/BFY8HStRbnrVLmMc3koCIFIQUcnO 11 | SQKwHA0C2uwooHPutTr+osBCiaOppYGZjaFc 12 | -----END CERTIFICATE----- 13 | -----BEGIN CERTIFICATE----- 14 | MIIB/TCCAaOgAwIBAgIJAJ/P8XnBz/Z0MAoGCCqGSM49BAMCMFoxCzAJBgNVBAYT 15 | AkRLMRMwEQYDVQQIDApTb21lLVN0YXRlMREwDwYDVQQHDAhSb3NraWxkZTEQMA4G 16 | A1UECgwHUmlzw4PCuDERMA8GA1UEAwwIT0VNIHJvb3QwIBcNMTUwNTEwMTgwMTI4 17 | WhgPMjI4OTAyMjExODAxMjhaMFoxCzAJBgNVBAYTAkRLMRMwEQYDVQQIDApTb21l 18 | LVN0YXRlMREwDwYDVQQHDAhSb3NraWxkZTEQMA4GA1UECgwHUmlzw4PCuDERMA8G 19 | A1UEAwwIT0VNIHJvb3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARhv9nYoTa3 20 | mCoY+6MwD/0aHYwhbdd/0O5ya100J9SnzsP1gRqeDk3gB0oVNNk2BbWMgJZHhpyS 21 | YkBBr/mS0Z+fo1AwTjAdBgNVHQ4EFgQUn9zTiVebQ38JNQOqN1C8KOr0w0QwHwYD 22 | VR0jBBgwFoAUn9zTiVebQ38JNQOqN1C8KOr0w0QwDAYDVR0TBAUwAwEB/zAKBggq 23 | hkjOPQQDAgNIADBFAiEA7hq1wCcLuBKJE0bzLYGT8BwFEppdO48Ht6S2iG2k94IC 24 | IAguWavd79xHlGI4n9GRfk+fEwDp81wddFM4P+4tVHIv 25 | -----END CERTIFICATE----- 26 | -------------------------------------------------------------------------------- /example/certs/evse (copy).pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBezCCASICCQDGlkCcWre0IzAKBggqhkjOPQQDAjBFMQswCQYDVQQGEwJESzER 3 | MA8GA1UEBwwIUm9za2lsZGUxFDASBgNVBAoMC0RUVSBSaXPDg8K4MQ0wCwYDVQQD 4 | DARldnNlMCAXDTE1MDQwNDAxMTcxNVoYDzQ3NTMwMjI4MDExNzE1WjBFMQswCQYD 5 | VQQGEwJESzERMA8GA1UEBwwIUm9za2lsZGUxFDASBgNVBAoMC0RUVSBSaXPDg8K4 6 | MQ0wCwYDVQQDDARldnNlMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEhW5Z3Aea 7 | 2fRl/l/somKIsIW5WREJ5/Kv0ukIj/8KERMb5SchqNpAJd/LrVaOzxTiJJcf57UO 8 | 5RkjRCsKjpyblDAKBggqhkjOPQQDAgNHADBEAiAaWh3wX6aHiP/+XW4vm/349/ib 9 | inkc0WP+sZ1ppOSEsQIgPedYvAl3x+O48/08Vpt+M0/NII7Ya4p5aoFIozAJGNY= 10 | -----END CERTIFICATE----- 11 | -------------------------------------------------------------------------------- /example/certs/evse.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIIBADCBpwIBADBFMQswCQYDVQQGEwJESzERMA8GA1UEBwwIUm9za2lsZGUxFDAS 3 | BgNVBAoMC0RUVSBSaXPDg8K4MQ0wCwYDVQQDDARldnNlMFkwEwYHKoZIzj0CAQYI 4 | KoZIzj0DAQcDQgAEhW5Z3Aea2fRl/l/somKIsIW5WREJ5/Kv0ukIj/8KERMb5Sch 5 | qNpAJd/LrVaOzxTiJJcf57UO5RkjRCsKjpyblKAAMAoGCCqGSM49BAMCA0gAMEUC 6 | IQDv58pwK5s/FAjli40iQr1f+Qmfm4IDiIoSzpVGnBd6lAIgD3ZVAizGB7NoMeS9 7 | Yltn6Ux2OsHWpXCxE66UUBNYnzk= 8 | -----END CERTIFICATE REQUEST----- 9 | -------------------------------------------------------------------------------- /example/certs/evse.key: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PARAMETERS----- 2 | BggqhkjOPQMBBw== 3 | -----END EC PARAMETERS----- 4 | -----BEGIN EC PRIVATE KEY----- 5 | MHcCAQEEICP9kptSu9f/umH2cZyBHOThPcIO6vrliWW+1nPjoyBzoAoGCCqGSM49 6 | AwEHoUQDQgAEhW5Z3Aea2fRl/l/somKIsIW5WREJ5/Kv0ukIj/8KERMb5SchqNpA 7 | Jd/LrVaOzxTiJJcf57UO5RkjRCsKjpyblA== 8 | -----END EC PRIVATE KEY----- 9 | -------------------------------------------------------------------------------- /example/certs/evse.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIByjCCAXCgAwIBAgIBAjAKBggqhkjOPQQDAjBaMQswCQYDVQQGEwJESzETMBEG 3 | A1UECAwKU29tZS1TdGF0ZTERMA8GA1UEBwwIUm9za2lsZGUxEDAOBgNVBAoMB1Jp 4 | c8ODwrgxETAPBgNVBAMMCFYyRyByb290MCAXDTE1MDUxMDE2NTY0M1oYDzIyODkw 5 | MjIxMTY1NjQzWjAyMQswCQYDVQQGEwJESzEUMBIGA1UECgwLRFRVIFJpc8ODwrgx 6 | DTALBgNVBAMMBGV2c2UwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASFblncB5rZ 7 | 9GX+X+yiYoiwhblZEQnn8q/S6QiP/woRExvlJyGo2kAl38utVo7PFOIklx/ntQ7l 8 | GSNEKwqOnJuUo00wSzAJBgNVHRMEAjAAMB0GA1UdDgQWBBT6fTlU2p85zin8lplu 9 | 2VP1iCZqCjAfBgNVHSMEGDAWgBTx81W84Vsv69BTxCTuwH+xtpz/ozAKBggqhkjO 10 | PQQDAgNIADBFAiBpNvWUK9nBo/GFXFehRt0jWEInp+UWBtOmGxby/l505QIhAOOl 11 | Ctt58sPyq+Db1ofxVAhnwiv21Cc/yEXtV+58tnSd 12 | -----END CERTIFICATE----- 13 | -----BEGIN CERTIFICATE----- 14 | MIIB/TCCAaOgAwIBAgIJAIkuKTqYYde0MAoGCCqGSM49BAMCMFoxCzAJBgNVBAYT 15 | AkRLMRMwEQYDVQQIDApTb21lLVN0YXRlMREwDwYDVQQHDAhSb3NraWxkZTEQMA4G 16 | A1UECgwHUmlzw4PCuDERMA8GA1UEAwwIVjJHIHJvb3QwIBcNMTUwNTEwMTYyNTMz 17 | WhgPMjI4OTAyMjExNjI1MzNaMFoxCzAJBgNVBAYTAkRLMRMwEQYDVQQIDApTb21l 18 | LVN0YXRlMREwDwYDVQQHDAhSb3NraWxkZTEQMA4GA1UECgwHUmlzw4PCuDERMA8G 19 | A1UEAwwIVjJHIHJvb3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASKc98tKgj/ 20 | 7cpK2ReCbB9K8taYyr4Ubm//cqsViCvpyCZtDFcRJH7hCtt7wH7NTwRDFG82zbt5 21 | OiE37wU+4TbTo1AwTjAdBgNVHQ4EFgQU8fNVvOFbL+vQU8Qk7sB/sbac/6MwHwYD 22 | VR0jBBgwFoAU8fNVvOFbL+vQU8Qk7sB/sbac/6MwDAYDVR0TBAUwAwEB/zAKBggq 23 | hkjOPQQDAgNIADBFAiBkY4ynVTX2QrvGiRe1LqOvAVL48+ccakvWa50TUalvugIh 24 | AJmiKoxs9TnpcxXvr/GqJjYWqVmhWHDb4nTfZIaKmgpN 25 | -----END CERTIFICATE----- 26 | -------------------------------------------------------------------------------- /example/certs/index.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cblach/nikola-v2gstack/5609b011fd8a4edfa27ee9afabcd7251dc4bcece/example/certs/index.txt -------------------------------------------------------------------------------- /example/certs/nano.save: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/certs/readme.txt: -------------------------------------------------------------------------------- 1 | ============================================ 2 | Create self-signed SSL certficiate for EVSE: 3 | ============================================ 4 | 1) Create .csr and .key, Sign with root certificate and concatenate certificates to a chain: 5 | 6 | openssl ecparam -name secp256r1 -genkey -out evse.key 7 | openssl req -new -sha256 -key evse.key -out evse.csr -subj "/C=DK/ST=/L=Roskilde/O=DTU Risø/CN=evse" 8 | 9 | openssl ca -keyfile root/v2g/keys/v2g.key \ 10 | -cert root/v2g/certs/v2g.pem \ 11 | -extensions usr_cert -notext -md sha256 -config ca/v2g/openssl.cfg \ 12 | -in evse.csr -out evse.pem -outdir . -verbose 13 | openssl verify -CAfile root/v2g/certs/v2g.pem evse.pem 14 | cat root/v2g/certs/v2g.pem >> evse.pem 15 | ================ 16 | EV equivalent: 17 | ================ 18 | openssl ecparam -name secp256r1 -genkey -out ev.key 19 | openssl req -new -sha256 -key ev.key -out ev.csr -subj "/C=DK/ST=/L=Roskilde/O=DTU Risø/CN=ev" 20 | openssl ca -keyfile root/oem/keys/oem.key \ 21 | -cert root/oem/certs/oem.pem \ 22 | -extensions usr_cert -notext -md sha256 -config ca/oem/openssl.cfg \ 23 | -in ev.csr -out ev.pem -outdir . -verbose 24 | 25 | 26 | ================ 27 | Mobility operator root certificate (contract root): 28 | ================ 29 | openssl ecparam -name secp256r1 -genkey -out root/mobilityop/keys/mobilityop.key 30 | openssl req -new -sha256 -key root/mobilityop/keys/mobilityop.key \ 31 | -out root/mobilityop/certs/mobilityop.pem \ 32 | -subj "/C=DK/ST=/L=Roskilde/O=DTU Risø/CN=Mobility operator root" \ 33 | -days 999999 -x509 34 | 35 | ================ 36 | Contract signed by mobility operator root: 37 | ================ 38 | openssl ecparam -name secp256r1 -genkey -out contract.key 39 | openssl req -new -sha256 -key contract.key -out contract.csr -subj "/C=DK/ST=/L=Roskilde/O=DTU Risø/CN=contract" 40 | openssl ca -keyfile root/mobilityop/keys/mobilityop.key \ 41 | -cert root/mobilityop/certs/mobilityop.pem \ 42 | -extensions usr_cert -notext -md sha256 -config ca/mobilityop/openssl.cfg \ 43 | -in contract.csr -out contract.pem -outdir . -verbose 44 | 45 | openssl verify -CAfile root/mobilityop/certs/mobilityop.pem contract.pem 46 | -------------------------------------------------------------------------------- /example/certs/root/mobilityop/certs/mobilityop.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIB9zCCAZ2gAwIBAgIJAMx6i/edGHdhMAoGCCqGSM49BAMCMFcxCzAJBgNVBAYT 3 | AkRLMREwDwYDVQQHDAhSb3NraWxkZTEUMBIGA1UECgwLRFRVIFJpc8ODwrgxHzAd 4 | BgNVBAMMFk1vYmlsaXR5IG9wZXJhdG9yIHJvb3QwIBcNMTUwNDA0MDcxNzQ1WhgP 5 | NDc1MzAyMjgwNzE3NDVaMFcxCzAJBgNVBAYTAkRLMREwDwYDVQQHDAhSb3NraWxk 6 | ZTEUMBIGA1UECgwLRFRVIFJpc8ODwrgxHzAdBgNVBAMMFk1vYmlsaXR5IG9wZXJh 7 | dG9yIHJvb3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARHuL8eWXTyDtW/gdPu 8 | pTUFP083ajkOyWSmMnSfs05Zb4oJET/y7Xl2OBtoEl+RG5jm9RugPPxhJ/J5EKrM 9 | +jzdo1AwTjAdBgNVHQ4EFgQUX4X5Evu1gwSG4EWFgb3hHgeKqcIwHwYDVR0jBBgw 10 | FoAUX4X5Evu1gwSG4EWFgb3hHgeKqcIwDAYDVR0TBAUwAwEB/zAKBggqhkjOPQQD 11 | AgNIADBFAiBJBQycWHJqv8dqoqlEgvbkk4KwFlq8SpVzSdwQbKTGvAIhAOJ5IR45 12 | P3rM+nDfA919SKB1oVwGe098mWuFbMybeL7h 13 | -----END CERTIFICATE----- 14 | -------------------------------------------------------------------------------- /example/certs/root/mobilityop/index.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /example/certs/root/mobilityop/index.txt.save: -------------------------------------------------------------------------------- 1 | [ ca ] 2 | default_ca = CA_default # The default ca section 3 | 4 | [ CA_default ] 5 | 6 | dir = ./demoCA # top dir 7 | database = $dir/index.txt # index file. 8 | new_certs_dir = $dir/newcerts # new certs dir 9 | 10 | certificate = $dir/cacert.pem # The CA cert 11 | serial = $dir/serial # serial no file 12 | private_key = $dir/private/cakey.pem# CA private key 13 | RANDFILE = $dir/private/.rand # random number file 14 | 15 | default_days = 365 # how long to certify for 16 | default_crl_days= 30 # how long before next CRL 17 | default_md = md5 # md to use 18 | 19 | policy = policy_any # default policy 20 | email_in_dn = no # Don't add the email into cert DN 21 | 22 | name_opt = ca_default # Subject name display option 23 | cert_opt = ca_default # Certificate display option 24 | copy_extensions = none # Don't copy extensions from request 25 | 26 | [ policy_any ] 27 | countryName = supplied 28 | stateOrProvinceName = optional 29 | organizationName = optional 30 | organizationalUnitName = optional 31 | commonName = supplied 32 | emailAddress = optional 33 | -------------------------------------------------------------------------------- /example/certs/root/mobilityop/keys/mobilityop.key: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PARAMETERS----- 2 | BggqhkjOPQMBBw== 3 | -----END EC PARAMETERS----- 4 | -----BEGIN EC PRIVATE KEY----- 5 | MHcCAQEEIKX8vwuvlbHs7iiMoIxNJtq/xJASLoENQ9OtAmLB8Z+voAoGCCqGSM49 6 | AwEHoUQDQgAER7i/Hll08g7Vv4HT7qU1BT9PN2o5DslkpjJ0n7NOWW+KCRE/8u15 7 | djgbaBJfkRuY5vUboDz8YSfyeRCqzPo83Q== 8 | -----END EC PRIVATE KEY----- 9 | -------------------------------------------------------------------------------- /example/certs/root/mobilityop/openssl.cfg: -------------------------------------------------------------------------------- 1 | [ ca ] 2 | default_ca = CA_default # The default ca section 3 | 4 | [ CA_default ] 5 | 6 | dir = . # top dir 7 | database = $dir/index.txt # index file. 8 | new_certs_dir = $dir # new certs dir 9 | 10 | certificate = $dir/certs/.pem # The CA cert 11 | serial = $dir/serial # serial no file 12 | private_key = $dir/private/cakey.pem# CA private key 13 | RANDFILE = $dir/private/.rand # random number file 14 | 15 | default_days = 999999 # how long to certify for 16 | default_crl_days= 30 # how long before next CRL 17 | default_md = sha256 # md to use 18 | 19 | policy = policy_any # default policy 20 | email_in_dn = no # Don't add the email into cert DN 21 | 22 | name_opt = ca_default # Subject name display option 23 | cert_opt = ca_default # Certificate display option 24 | copy_extensions = none # Don't copy extensions from request 25 | 26 | [ policy_any ] 27 | countryName = supplied 28 | stateOrProvinceName = optional 29 | organizationName = optional 30 | organizationalUnitName = optional 31 | commonName = supplied 32 | emailAddress = optional 33 | -------------------------------------------------------------------------------- /example/certs/root/oem/certs/oem.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIB/TCCAaOgAwIBAgIJAJ/P8XnBz/Z0MAoGCCqGSM49BAMCMFoxCzAJBgNVBAYT 3 | AkRLMRMwEQYDVQQIDApTb21lLVN0YXRlMREwDwYDVQQHDAhSb3NraWxkZTEQMA4G 4 | A1UECgwHUmlzw4PCuDERMA8GA1UEAwwIT0VNIHJvb3QwIBcNMTUwNTEwMTgwMTI4 5 | WhgPMjI4OTAyMjExODAxMjhaMFoxCzAJBgNVBAYTAkRLMRMwEQYDVQQIDApTb21l 6 | LVN0YXRlMREwDwYDVQQHDAhSb3NraWxkZTEQMA4GA1UECgwHUmlzw4PCuDERMA8G 7 | A1UEAwwIT0VNIHJvb3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARhv9nYoTa3 8 | mCoY+6MwD/0aHYwhbdd/0O5ya100J9SnzsP1gRqeDk3gB0oVNNk2BbWMgJZHhpyS 9 | YkBBr/mS0Z+fo1AwTjAdBgNVHQ4EFgQUn9zTiVebQ38JNQOqN1C8KOr0w0QwHwYD 10 | VR0jBBgwFoAUn9zTiVebQ38JNQOqN1C8KOr0w0QwDAYDVR0TBAUwAwEB/zAKBggq 11 | hkjOPQQDAgNIADBFAiEA7hq1wCcLuBKJE0bzLYGT8BwFEppdO48Ht6S2iG2k94IC 12 | IAguWavd79xHlGI4n9GRfk+fEwDp81wddFM4P+4tVHIv 13 | -----END CERTIFICATE----- 14 | -------------------------------------------------------------------------------- /example/certs/root/oem/index.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /example/certs/root/oem/index.txt.save: -------------------------------------------------------------------------------- 1 | [ ca ] 2 | default_ca = CA_default # The default ca section 3 | 4 | [ CA_default ] 5 | 6 | dir = ./demoCA # top dir 7 | database = $dir/index.txt # index file. 8 | new_certs_dir = $dir/newcerts # new certs dir 9 | 10 | certificate = $dir/cacert.pem # The CA cert 11 | serial = $dir/serial # serial no file 12 | private_key = $dir/private/cakey.pem# CA private key 13 | RANDFILE = $dir/private/.rand # random number file 14 | 15 | default_days = 365 # how long to certify for 16 | default_crl_days= 30 # how long before next CRL 17 | default_md = md5 # md to use 18 | 19 | policy = policy_any # default policy 20 | email_in_dn = no # Don't add the email into cert DN 21 | 22 | name_opt = ca_default # Subject name display option 23 | cert_opt = ca_default # Certificate display option 24 | copy_extensions = none # Don't copy extensions from request 25 | 26 | [ policy_any ] 27 | countryName = supplied 28 | stateOrProvinceName = optional 29 | organizationName = optional 30 | organizationalUnitName = optional 31 | commonName = supplied 32 | emailAddress = optional 33 | -------------------------------------------------------------------------------- /example/certs/root/oem/keys/oem.key: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PARAMETERS----- 2 | BggqhkjOPQMBBw== 3 | -----END EC PARAMETERS----- 4 | -----BEGIN EC PRIVATE KEY----- 5 | MHcCAQEEIPEEszUIq9ap3QwurrKMH210TaFN/MahOohWKQZ/pd/XoAoGCCqGSM49 6 | AwEHoUQDQgAEYb/Z2KE2t5gqGPujMA/9Gh2MIW3Xf9DucmtdNCfUp87D9YEang5N 7 | 4AdKFTTZNgW1jICWR4ackmJAQa/5ktGfnw== 8 | -----END EC PRIVATE KEY----- 9 | -------------------------------------------------------------------------------- /example/certs/root/oem/openssl.cfg: -------------------------------------------------------------------------------- 1 | [ ca ] 2 | default_ca = CA_default # The default ca section 3 | 4 | [ CA_default ] 5 | 6 | dir = . # top dir 7 | database = $dir/index.txt # index file. 8 | new_certs_dir = $dir # new certs dir 9 | 10 | certificate = $dir/certs/.pem # The CA cert 11 | serial = $dir/serial # serial no file 12 | private_key = $dir/private/cakey.pem# CA private key 13 | RANDFILE = $dir/private/.rand # random number file 14 | 15 | default_days = 999999 # how long to certify for 16 | default_crl_days= 30 # how long before next CRL 17 | default_md = sha256 # md to use 18 | 19 | policy = policy_any # default policy 20 | email_in_dn = no # Don't add the email into cert DN 21 | 22 | name_opt = ca_default # Subject name display option 23 | cert_opt = ca_default # Certificate display option 24 | copy_extensions = none # Don't copy extensions from request 25 | 26 | [ policy_any ] 27 | countryName = supplied 28 | stateOrProvinceName = optional 29 | organizationName = optional 30 | organizationalUnitName = optional 31 | commonName = supplied 32 | emailAddress = optional 33 | -------------------------------------------------------------------------------- /example/certs/root/v2g/certs/v2g.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIB/TCCAaOgAwIBAgIJAIkuKTqYYde0MAoGCCqGSM49BAMCMFoxCzAJBgNVBAYT 3 | AkRLMRMwEQYDVQQIDApTb21lLVN0YXRlMREwDwYDVQQHDAhSb3NraWxkZTEQMA4G 4 | A1UECgwHUmlzw4PCuDERMA8GA1UEAwwIVjJHIHJvb3QwIBcNMTUwNTEwMTYyNTMz 5 | WhgPMjI4OTAyMjExNjI1MzNaMFoxCzAJBgNVBAYTAkRLMRMwEQYDVQQIDApTb21l 6 | LVN0YXRlMREwDwYDVQQHDAhSb3NraWxkZTEQMA4GA1UECgwHUmlzw4PCuDERMA8G 7 | A1UEAwwIVjJHIHJvb3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASKc98tKgj/ 8 | 7cpK2ReCbB9K8taYyr4Ubm//cqsViCvpyCZtDFcRJH7hCtt7wH7NTwRDFG82zbt5 9 | OiE37wU+4TbTo1AwTjAdBgNVHQ4EFgQU8fNVvOFbL+vQU8Qk7sB/sbac/6MwHwYD 10 | VR0jBBgwFoAU8fNVvOFbL+vQU8Qk7sB/sbac/6MwDAYDVR0TBAUwAwEB/zAKBggq 11 | hkjOPQQDAgNIADBFAiBkY4ynVTX2QrvGiRe1LqOvAVL48+ccakvWa50TUalvugIh 12 | AJmiKoxs9TnpcxXvr/GqJjYWqVmhWHDb4nTfZIaKmgpN 13 | -----END CERTIFICATE----- 14 | -------------------------------------------------------------------------------- /example/certs/root/v2g/index.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /example/certs/root/v2g/index.txt.save: -------------------------------------------------------------------------------- 1 | [ ca ] 2 | default_ca = CA_default # The default ca section 3 | 4 | [ CA_default ] 5 | 6 | dir = ./demoCA # top dir 7 | database = $dir/index.txt # index file. 8 | new_certs_dir = $dir/newcerts # new certs dir 9 | 10 | certificate = $dir/cacert.pem # The CA cert 11 | serial = $dir/serial # serial no file 12 | private_key = $dir/private/cakey.pem# CA private key 13 | RANDFILE = $dir/private/.rand # random number file 14 | 15 | default_days = 365 # how long to certify for 16 | default_crl_days= 30 # how long before next CRL 17 | default_md = md5 # md to use 18 | 19 | policy = policy_any # default policy 20 | email_in_dn = no # Don't add the email into cert DN 21 | 22 | name_opt = ca_default # Subject name display option 23 | cert_opt = ca_default # Certificate display option 24 | copy_extensions = none # Don't copy extensions from request 25 | 26 | [ policy_any ] 27 | countryName = supplied 28 | stateOrProvinceName = optional 29 | organizationName = optional 30 | organizationalUnitName = optional 31 | commonName = supplied 32 | emailAddress = optional 33 | -------------------------------------------------------------------------------- /example/certs/root/v2g/keys/v2g.key: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PARAMETERS----- 2 | BggqhkjOPQMBBw== 3 | -----END EC PARAMETERS----- 4 | -----BEGIN EC PRIVATE KEY----- 5 | MHcCAQEEIEZ7AnIpIaRTToUnhWA1eqnIsy5xtYS51Mv1/wMTxsWpoAoGCCqGSM49 6 | AwEHoUQDQgAEinPfLSoI/+3KStkXgmwfSvLWmMq+FG5v/3KrFYgr6cgmbQxXESR+ 7 | 4Qrbe8B+zU8EQxRvNs27eTohN+8FPuE20w== 8 | -----END EC PRIVATE KEY----- 9 | -------------------------------------------------------------------------------- /example/certs/root/v2g/openssl.cfg: -------------------------------------------------------------------------------- 1 | [ ca ] 2 | default_ca = CA_default # The default ca section 3 | 4 | [ CA_default ] 5 | 6 | dir = . # top dir 7 | database = $dir/index.txt # index file. 8 | new_certs_dir = $dir # new certs dir 9 | 10 | certificate = $dir/certs/.pem # The CA cert 11 | serial = $dir/serial # serial no file 12 | private_key = $dir/private/cakey.pem# CA private key 13 | RANDFILE = $dir/private/.rand # random number file 14 | 15 | default_days = 999999 # how long to certify for 16 | default_crl_days= 30 # how long before next CRL 17 | default_md = sha256 # md to use 18 | 19 | policy = policy_any # default policy 20 | email_in_dn = no # Don't add the email into cert DN 21 | 22 | name_opt = ca_default # Subject name display option 23 | cert_opt = ca_default # Certificate display option 24 | copy_extensions = none # Don't copy extensions from request 25 | 26 | [ policy_any ] 27 | countryName = supplied 28 | stateOrProvinceName = optional 29 | organizationName = optional 30 | organizationalUnitName = optional 31 | commonName = supplied 32 | emailAddress = optional 33 | -------------------------------------------------------------------------------- /example/client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "client.h" 17 | 18 | //========================================= 19 | // Utility Functions 20 | //========================================= 21 | 22 | void evcc_session_cleanup(ev_session_t* s) { 23 | ctr_drbg_free(&s->contract.ctr_drbg); 24 | ecdsa_free(&s->contract.key); 25 | entropy_free(&s->contract.entropy); 26 | } 27 | 28 | int load_contract(const char *pemchain_path, 29 | const char *keyfile_path, 30 | ev_session_t *s) { 31 | int err, i = 0; 32 | x509_crt crtchain; 33 | x509_crt* crt; 34 | pk_context pk; 35 | pk_init(&pk); 36 | const char *pers = "ecdsa"; 37 | x509_crt_init(&crtchain); 38 | err = x509_crt_parse_file(&crtchain, pemchain_path); 39 | if (err != 0) { 40 | printf("load_contract: x509_crl_parse_file error\n"); 41 | return -1; 42 | } 43 | if (crtchain.raw.len > v2gCertificateChainType_Certificate_BYTES_SIZE) { 44 | printf("load_contract: certificate too big\n"); 45 | return -1; 46 | } 47 | memcpy(&s->contract.cert, crtchain.raw.p, crtchain.raw.len); 48 | s->contract.cert_len = crtchain.raw.len; 49 | crt = &crtchain; 50 | while (crt->next != NULL) { 51 | if (i > v2gSubCertificatesType_Certificate_ARRAY_SIZE) { 52 | printf("load_contract: certificate chain too long (max 4 subcerts)\n"); 53 | return -1; 54 | } 55 | crt = crt->next; 56 | if (crt->raw.len > v2gSubCertificatesType_Certificate_BYTES_SIZE) { 57 | printf("load_contract: subcertificate too big\n"); 58 | return -1; 59 | } 60 | memcpy(&s->contract.sub_certs[i], crt->raw.p, crt->raw.len); 61 | s->contract.subcert_len[i] = crt->raw.len; 62 | i++; 63 | } 64 | x509_crt_free(&crtchain); 65 | err = pk_parse_keyfile(&pk, keyfile_path, NULL); 66 | if (err != 0) { 67 | printf("could not parse keyfile at %s\n",keyfile_path); 68 | return -1; 69 | } 70 | ecp_keypair *kp = pk_ec(pk); 71 | ecdsa_free(&s->contract.key); // Free, if existing already 72 | err = ecdsa_from_keypair(&s->contract.key, kp); 73 | pk_free(&pk); 74 | if (err != 0) { 75 | printf("could not retrieve ecdsa from keypair at %s\n",keyfile_path); 76 | return -1; 77 | } 78 | 79 | entropy_init(&s->contract.entropy); 80 | if ((err = ctr_drbg_init(&s->contract.ctr_drbg, entropy_func, 81 | &s->contract.entropy, 82 | (const unsigned char*)pers, 83 | strlen(pers))) != 0) { 84 | printf("load_contract: failed\n ! ctr_drbg_init returned %d\n", err); 85 | return -1; 86 | } 87 | return 0; 88 | } 89 | 90 | int sign_auth_request(struct v2gAuthorizationReqType *req, 91 | ecdsa_context *key, 92 | ctr_drbg_context *ctr_drbg, 93 | struct v2gSignatureType *sig) { 94 | int err; 95 | unsigned char buf[256]; 96 | uint8_t digest[32]; 97 | uint16_t buffer_pos = 0; 98 | bitstream_t stream = { 99 | .size = 256, 100 | .data = buf, 101 | .pos = &buffer_pos, 102 | .buffer = 0, 103 | .capacity = 8, // Set to 8 for send and 0 for recv 104 | }; 105 | //struct v2gEXIDocument exiIn; 106 | struct v2gEXIFragment auth_fragment; 107 | init_v2gEXIFragment(&auth_fragment); 108 | auth_fragment.AuthorizationReq_isUsed = 1u; 109 | memcpy(&auth_fragment.AuthorizationReq, req, sizeof(struct v2gAuthorizationReqType)); 110 | err = encode_v2gExiFragment(&stream, &auth_fragment); 111 | if (err != 0) { 112 | printf("error 1: error code = %d\n", err); 113 | return -1; 114 | } 115 | sha256(buf, (size_t)buffer_pos, digest, 0); 116 | //======================================= 117 | // Create signature 118 | //======================================= 119 | struct xmldsigEXIFragment sig_fragment; 120 | memset(&sig_fragment, 0, sizeof(sig_fragment)); 121 | struct xmldsigReferenceType *ref = &sig_fragment.SignedInfo.Reference.array[0]; 122 | char uri[4] = {"#ID1"}; 123 | char arrayCanonicalEXI[35] = {"http://www.w3.org/TR/canonical-exi/"}; 124 | char arrayxmldsigSHA256[51] = {"http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256"}; 125 | char arrayxmlencSHA256[39] = {"http://www.w3.org/2001/04/xmlenc#sha256"}; 126 | init_xmldsigEXIFragment(&sig_fragment); 127 | sig_fragment.SignedInfo_isUsed = 1; 128 | init_xmldsigSignedInfoType(&sig_fragment.SignedInfo); 129 | init_xmldsigCanonicalizationMethodType(&sig_fragment.SignedInfo.CanonicalizationMethod); 130 | sig_fragment.SignedInfo.CanonicalizationMethod.Algorithm.charactersLen = 35; 131 | memcpy(sig_fragment.SignedInfo.CanonicalizationMethod.Algorithm.characters, arrayCanonicalEXI, 35); 132 | sig_fragment.SignedInfo.SignatureMethod.HMACOutputLength_isUsed = 0; 133 | sig_fragment.SignedInfo.SignatureMethod.Algorithm.charactersLen = 51; 134 | strncpy(sig_fragment.SignedInfo.SignatureMethod.Algorithm.characters, arrayxmldsigSHA256, 51); 135 | sig_fragment.SignedInfo.Reference.arrayLen = 1; 136 | ref->URI_isUsed = 1; 137 | ref->URI.charactersLen = 4; 138 | memcpy(ref->URI.characters, uri, 4); 139 | // "http://www.w3.org/TR/canonical-exi/" 140 | ref->Transforms_isUsed = 1; 141 | ref->Transforms.Transform.arrayLen = 1; 142 | ref->Transforms.Transform.array[0].Algorithm.charactersLen = 35; 143 | strncpy(ref->Transforms.Transform.array[0].Algorithm.characters, arrayCanonicalEXI, 35); // Will copy 35 characters from arrayCanonicalEXI to characters 144 | ref->Transforms.Transform.array[0].XPath.arrayLen = 0; 145 | ref->DigestMethod.Algorithm.charactersLen = 39; 146 | strncpy(ref->DigestMethod.Algorithm.characters, arrayxmlencSHA256, 39); 147 | ref->DigestValue.bytesLen = 32; 148 | memcpy(ref->DigestValue.bytes, digest, 32); 149 | buffer_pos = 0; 150 | err = encode_xmldsigExiFragment(&stream, &sig_fragment); 151 | if (err != 0) { 152 | printf("error 2: error code = %d\n", err); 153 | return -1; 154 | } 155 | memcpy(&sig->SignedInfo, &sig_fragment.SignedInfo, sizeof(struct v2gSignedInfoType)); 156 | sha256(buf, buffer_pos, digest, 0); 157 | err = ecdsa_write_signature(key, 158 | digest, 32, 159 | sig->SignatureValue.CONTENT.bytes, 160 | (size_t*)&sig->SignatureValue.CONTENT.bytesLen, 161 | ctr_drbg_random, 162 | ctr_drbg); 163 | if (err != 0) { 164 | printf("ecdsa write sig err\n"); 165 | return -1; 166 | } 167 | sig->KeyInfo_isUsed = 0; 168 | sig->Id_isUsed = 0; 169 | sig->Object.arrayLen = 1; 170 | sig->Object.array[0].Id_isUsed = 0; 171 | sig->Object.array[0].MimeType_isUsed = 0; 172 | sig->Object.array[0].Encoding_isUsed = 0; 173 | sig->SignatureValue.Id_isUsed = 0; 174 | return 0; 175 | } 176 | 177 | void SetProfileEntry(struct v2gChargingProfileType* prof, 178 | uint32_t start, int32_t value, 179 | uint32_t nphases) 180 | { 181 | uint16_t* counter = &prof->ProfileEntry.arrayLen; 182 | const int max_value = (2 << 16) - 1; 183 | const int max_power = (2 << 8) - 1; 184 | int power = 0; 185 | while(abs(value) > max_value && 186 | power < max_power) { 187 | value /= 10; 188 | power ++; 189 | } 190 | 191 | prof->ProfileEntry.array[*counter] = (struct v2gProfileEntryType) { 192 | .ChargingProfileEntryStart = start, 193 | .ChargingProfileEntryMaxNumberOfPhasesInUse = nphases, 194 | .ChargingProfileEntryMaxNumberOfPhasesInUse_isUsed = 1u, 195 | .ChargingProfileEntryMaxPower = (struct v2gPhysicalValueType) { 196 | .Value = value, 197 | .Multiplier = power, 198 | .Unit = v2gunitSymbolType_W, 199 | }, 200 | }; 201 | (*counter)++; 202 | } 203 | 204 | 205 | /*static void printACEVSEStatus(struct v2gAC_EVSEStatusType *status) 206 | { 207 | printf("\tEVSEStatus:\n"); 208 | printf("\t\tRCD=%d\n", status->RCD); 209 | printf("\t\tEVSENotification=%d\n", status->EVSENotification); 210 | printf("\t\tNotificationMaxDelay=%d\n", status->NotificationMaxDelay); 211 | }*/ 212 | 213 | int verify_response_code(v2gresponseCodeType code) 214 | { 215 | switch (code) { 216 | case v2gresponseCodeType_OK: 217 | return 0; 218 | case v2gresponseCodeType_OK_NewSessionEstablished: 219 | return 0; 220 | case v2gresponseCodeType_OK_OldSessionJoined: 221 | return 0; 222 | case v2gresponseCodeType_OK_CertificateExpiresSoon: 223 | return 0; 224 | case v2gresponseCodeType_FAILED: 225 | case v2gresponseCodeType_FAILED_SequenceError: 226 | case v2gresponseCodeType_FAILED_ServiceIDInvalid: 227 | case v2gresponseCodeType_FAILED_UnknownSession: 228 | case v2gresponseCodeType_FAILED_ServiceSelectionInvalid: 229 | case v2gresponseCodeType_FAILED_PaymentSelectionInvalid: 230 | case v2gresponseCodeType_FAILED_CertificateExpired: 231 | case v2gresponseCodeType_FAILED_SignatureError: 232 | case v2gresponseCodeType_FAILED_NoCertificateAvailable: 233 | case v2gresponseCodeType_FAILED_CertChainError: 234 | case v2gresponseCodeType_FAILED_ChallengeInvalid: 235 | case v2gresponseCodeType_FAILED_ContractCanceled: 236 | case v2gresponseCodeType_FAILED_WrongChargeParameter: 237 | case v2gresponseCodeType_FAILED_PowerDeliveryNotApplied: 238 | case v2gresponseCodeType_FAILED_TariffSelectionInvalid: 239 | case v2gresponseCodeType_FAILED_ChargingProfileInvalid: 240 | case v2gresponseCodeType_FAILED_MeteringSignatureNotValid: 241 | case v2gresponseCodeType_FAILED_NoChargeServiceSelected: 242 | case v2gresponseCodeType_FAILED_WrongEnergyTransferMode: 243 | case v2gresponseCodeType_FAILED_ContactorError: 244 | case v2gresponseCodeType__FAILED_CertificateNotAllowedAtThisEVSE: 245 | case v2gresponseCodeType_FAILED_CertificateRevoked: 246 | default: 247 | return -1; 248 | } 249 | } 250 | 251 | 252 | void init_v2g_request(struct v2gEXIDocument *exiIn, ev_session_t *s) 253 | { 254 | memset(exiIn, 0, sizeof(*exiIn)); 255 | init_v2gEXIDocument(exiIn); 256 | exiIn->V2G_Message_isUsed = 1u; 257 | init_v2gMessageHeaderType(&exiIn->V2G_Message.Header); 258 | // Set session id to 0 259 | if (s == NULL) { 260 | memset(exiIn->V2G_Message.Header.SessionID.bytes, 0, 8); 261 | } else { 262 | memcpy(exiIn->V2G_Message.Header.SessionID.bytes, &s->id, 8); 263 | } 264 | exiIn->V2G_Message.Header.SessionID.bytesLen = 8; 265 | exiIn->V2G_Message.Header.Notification_isUsed = 0u; // no notification 266 | exiIn->V2G_Message.Header.Signature_isUsed = 0u; 267 | init_v2gBodyType(&exiIn->V2G_Message.Body); 268 | } 269 | 270 | //======================= 271 | // Request Definitions 272 | //======================= 273 | int session_request(evcc_conn_t *conn, ev_session_t *s) 274 | { 275 | int err; 276 | struct v2gEXIDocument exiIn; 277 | struct v2gEXIDocument exiOut; 278 | init_v2g_request(&exiIn, NULL); 279 | exiIn.V2G_Message.Body.SessionSetupReq_isUsed = 1u; 280 | 281 | init_v2gSessionSetupReqType(&exiIn.V2G_Message.Body.SessionSetupReq); 282 | 283 | exiIn.V2G_Message.Body.SessionSetupReq.EVCCID.bytesLen = 1; 284 | exiIn.V2G_Message.Body.SessionSetupReq.EVCCID.bytes[0] = 20; 285 | err = v2g_request(conn, &exiIn, &exiOut); 286 | if (err != 0) { 287 | printf("unable to do v2g_request, exiting\n"); 288 | return -1; 289 | } 290 | // === Validate response type === 291 | if (exiOut.V2G_Message.Body.SessionSetupRes_isUsed != 1u) { 292 | printf("session_request: wrong response type\n"); 293 | return -1; 294 | } 295 | // === Validate response code === 296 | if (verify_response_code(exiOut.V2G_Message.Body.SessionSetupRes.ResponseCode) != 0) { 297 | printf("session_request: session setup response NOT ok, code = %d\n", exiOut.V2G_Message.Body.SessionSetupRes.ResponseCode); 298 | return -1; 299 | } 300 | // === Save session id === 301 | memcpy(&s->id, exiOut.V2G_Message.Header.SessionID.bytes, 8); 302 | 303 | return 0; 304 | } 305 | 306 | int service_discovery_request(evcc_conn_t *conn, ev_session_t *s) 307 | { 308 | int err; 309 | struct v2gEXIDocument exiIn; 310 | struct v2gEXIDocument exiOut; 311 | // === Init === 312 | init_v2g_request(&exiIn, s); 313 | exiIn.V2G_Message.Body.ServiceDiscoveryReq_isUsed = 1u; 314 | init_v2gServiceDiscoveryReqType(&exiIn.V2G_Message.Body.ServiceDiscoveryReq); 315 | 316 | exiIn.V2G_Message.Body.ServiceDiscoveryReq.ServiceCategory_isUsed = 1u; 317 | exiIn.V2G_Message.Body.ServiceDiscoveryReq.ServiceCategory = v2gserviceCategoryType_EVCharging; 318 | exiIn.V2G_Message.Body.ServiceDiscoveryReq.ServiceScope_isUsed = 0u; 319 | err = v2g_request(conn, &exiIn, &exiOut); 320 | if (err != 0) { 321 | printf("unable to do v2g_request, exiting\n"); 322 | return -1; 323 | } 324 | // === Validate response type === 325 | if (exiOut.V2G_Message.Body.ServiceDiscoveryRes_isUsed != 1u) { 326 | printf("service_discovery_request: wrong response type\n"); 327 | return -1; 328 | } 329 | // === Validate response code === 330 | if (verify_response_code(exiOut.V2G_Message.Body.ServiceDiscoveryRes.ResponseCode) != 0) { 331 | printf("service_discovery_request: response NOT ok, code = %d\n", exiOut.V2G_Message.Body.ServiceDiscoveryRes.ResponseCode); 332 | return -1; 333 | } 334 | s->charge_service_id = exiOut.V2G_Message.Body.ServiceDiscoveryRes.ChargeService.ServiceID; 335 | s->charging_is_free = exiOut.V2G_Message.Body.ServiceDiscoveryRes.ChargeService.FreeService && 1; 336 | return 0; 337 | } 338 | 339 | int payment_selection_request(evcc_conn_t *conn, ev_session_t *s) 340 | { 341 | int err; 342 | struct v2gEXIDocument exiIn; 343 | struct v2gEXIDocument exiOut; 344 | init_v2g_request(&exiIn, s); 345 | init_v2gPaymentServiceSelectionReqType(&exiIn.V2G_Message.Body.PaymentServiceSelectionReq); 346 | exiIn.V2G_Message.Body.PaymentServiceSelectionReq_isUsed = 1u; 347 | 348 | if (!s->charging_is_free) { 349 | exiIn.V2G_Message.Body.PaymentServiceSelectionReq.SelectedPaymentOption = v2gpaymentOptionType_Contract; 350 | } 351 | exiIn.V2G_Message.Body.PaymentServiceSelectionReq.SelectedServiceList.SelectedService.arrayLen = 1; // === only one service was selected === 352 | exiIn.V2G_Message.Body.PaymentServiceSelectionReq.SelectedServiceList.SelectedService.array[0].ServiceID = s->charge_service_id; // charge server ID 353 | exiIn.V2G_Message.Body.PaymentServiceSelectionReq.SelectedServiceList.SelectedService.array[0].ParameterSetID_isUsed = 0u; // is not used 354 | 355 | 356 | err = v2g_request(conn, &exiIn, &exiOut); 357 | if (err != 0) { 358 | printf("unable to do payment_selection_request v2g_request, exiting\n"); 359 | return -1; 360 | } 361 | // === Validate response type === 362 | if (exiOut.V2G_Message.Body.PaymentServiceSelectionRes_isUsed != 1u) { 363 | printf("payment_selection_request: wrong response type\n"); 364 | return -1; 365 | } 366 | // === Validate response code === 367 | if (verify_response_code(exiOut.V2G_Message.Body.PaymentServiceSelectionRes.ResponseCode) != 0) { 368 | printf("payment_selection_request: response NOT ok, code = %d\n", exiOut.V2G_Message.Body.PaymentServiceSelectionRes.ResponseCode); 369 | return -1; 370 | } 371 | return 0; 372 | } 373 | 374 | int payment_details_request(evcc_conn_t *conn, ev_session_t *s) 375 | { 376 | int err; 377 | struct v2gEXIDocument exiIn; 378 | struct v2gEXIDocument exiOut; 379 | struct v2gPaymentDetailsReqType *req = &exiIn.V2G_Message.Body.PaymentDetailsReq; 380 | struct v2gPaymentDetailsResType *res = &exiOut.V2G_Message.Body.PaymentDetailsRes; 381 | init_v2g_request(&exiIn, s); 382 | init_v2gPaymentDetailsReqType(req); 383 | exiIn.V2G_Message.Body.PaymentDetailsReq_isUsed = 1u; 384 | req->eMAID.characters[0] = 1; 385 | req->eMAID.characters[1] = 123; 386 | req->eMAID.charactersLen = 2; 387 | if (s->contract.cert_len == 0) { 388 | printf("payment_details_request: contract certificate not loaded\n"); 389 | return -1; 390 | } 391 | memcpy(req->ContractSignatureCertChain.Certificate.bytes, s->contract.cert, s->contract.cert_len); 392 | req->ContractSignatureCertChain.Certificate.bytesLen = s->contract.cert_len; 393 | req->ContractSignatureCertChain.SubCertificates_isUsed = 1u; 394 | memcpy(req->ContractSignatureCertChain.SubCertificates.Certificate.array[0].bytes, s->contract.sub_certs[0], s->contract.subcert_len[0]); 395 | req->ContractSignatureCertChain.SubCertificates.Certificate.array[0].bytesLen = s->contract.subcert_len[0]; 396 | req->ContractSignatureCertChain.SubCertificates.Certificate.arrayLen = 1; 397 | req->ContractSignatureCertChain.Id_isUsed = 0; 398 | err = v2g_request(conn, &exiIn, &exiOut); 399 | if (err != 0) { 400 | printf("unable to do payment_details_request v2g_request, exiting\n"); 401 | return -1; 402 | } 403 | // === Validate response type === 404 | if (exiOut.V2G_Message.Body.PaymentDetailsRes_isUsed != 1u) { 405 | printf("payment_details_request: wrong response type\n"); 406 | return -1; 407 | } 408 | // === Validate response code === 409 | if (verify_response_code(res->ResponseCode) != 0) { 410 | printf("payment_details_request: session setup response NOT ok, code = %d\n", res->ResponseCode); 411 | return -1; 412 | } 413 | if (res->GenChallenge.bytesLen != 16) { 414 | printf("payment_details: Invalid genchallenge length %u, length must me 16\n", res->GenChallenge.bytesLen); 415 | return -1; 416 | } 417 | memcpy(s->challenge, res->GenChallenge.bytes, 16); 418 | return 0; 419 | } 420 | 421 | int authorization_request(evcc_conn_t *conn, ev_session_t *s) 422 | { 423 | int err; 424 | struct v2gEXIDocument exiIn; 425 | struct v2gEXIDocument exiOut; 426 | struct v2gAuthorizationReqType *req = &exiIn.V2G_Message.Body.AuthorizationReq; 427 | struct v2gAuthorizationResType *res = &exiOut.V2G_Message.Body.AuthorizationRes; 428 | init_v2g_request(&exiIn, s); 429 | init_v2gAuthorizationReqType(req); 430 | exiIn.V2G_Message.Body.AuthorizationReq_isUsed = 1u; 431 | req->Id_isUsed = 1u; 432 | req->Id.characters[0] = 'I'; 433 | req->Id.characters[1] = 'D'; 434 | req->Id.characters[2] = '1'; 435 | req->Id.charactersLen = 3; 436 | if (!s->charging_is_free) { 437 | req->GenChallenge_isUsed = 1; 438 | memcpy(req->GenChallenge.bytes, s->challenge, 16); 439 | req->GenChallenge.bytesLen = 16; 440 | 441 | exiIn.V2G_Message.Header.Signature_isUsed = 1u; 442 | sign_auth_request(req, &s->contract.key, 443 | &s->contract.ctr_drbg, 444 | &exiIn.V2G_Message.Header.Signature); 445 | } 446 | 447 | err = v2g_request(conn, &exiIn, &exiOut); 448 | if (err != 0) { 449 | printf("unable to do authorization v2g_request, exiting\n"); 450 | return -1; 451 | } 452 | // === Validate response type === 453 | if (exiOut.V2G_Message.Body.AuthorizationRes_isUsed != 1u) { 454 | printf("authorization_request: wrong response type\n"); 455 | return -1; 456 | } 457 | // === Validate response code === 458 | if (verify_response_code(res->ResponseCode) != 0) { 459 | printf("authorization_request: authorization response NOT ok, code = %d\n", res->ResponseCode); 460 | return -1; 461 | } 462 | if (res->EVSEProcessing != v2gEVSEProcessingType_Finished) { 463 | printf("\t EVSEProcessing=Not Finished\n"); 464 | return -1; 465 | } 466 | return 0; 467 | } 468 | 469 | int charge_parameter_request(evcc_conn_t *conn, ev_session_t *s) 470 | { 471 | int err; 472 | struct v2gEXIDocument exiIn; 473 | struct v2gEXIDocument exiOut; 474 | struct v2gChargeParameterDiscoveryReqType *req = &exiIn.V2G_Message.Body.ChargeParameterDiscoveryReq; 475 | struct v2gChargeParameterDiscoveryResType *res = &exiOut.V2G_Message.Body.ChargeParameterDiscoveryRes; 476 | struct v2gAC_EVChargeParameterType *charge_params = &req->AC_EVChargeParameter; 477 | init_v2g_request(&exiIn, s); 478 | exiIn.V2G_Message.Body.ChargeParameterDiscoveryReq_isUsed = 1u; 479 | init_v2gChargeParameterDiscoveryReqType(req); 480 | 481 | //=== we use here AC based charging parameters === 482 | req->RequestedEnergyTransferMode = v2gEnergyTransferModeType_AC_single_phase_core; 483 | req->MaxEntriesSAScheduleTuple = 1234; 484 | 485 | req->AC_EVChargeParameter_isUsed = 1u; 486 | 487 | *charge_params = (struct v2gAC_EVChargeParameterType) { 488 | .DepartureTime = 12345, 489 | .EAmount = (struct v2gPhysicalValueType) { 490 | .Value = 20, 491 | .Multiplier = 3, 492 | .Unit = v2gunitSymbolType_Wh, 493 | }, 494 | .EVMaxCurrent = (struct v2gPhysicalValueType) { 495 | .Value = 500, 496 | .Multiplier = 0, 497 | .Unit = v2gunitSymbolType_A, 498 | }, 499 | .EVMinCurrent = (struct v2gPhysicalValueType) { 500 | .Value = 200, 501 | .Multiplier = 0, 502 | .Unit = v2gunitSymbolType_A, 503 | }, 504 | .EVMaxVoltage = (struct v2gPhysicalValueType) { 505 | .Value = 400, 506 | .Multiplier = 0, 507 | .Unit = v2gunitSymbolType_A, 508 | }, 509 | }; 510 | 511 | err = v2g_request(conn, &exiIn, &exiOut); 512 | if (err != 0) { 513 | printf("unable to do charge_parameter v2g_request, exiting\n"); 514 | return -1; 515 | } 516 | // === Validate response type === 517 | if (exiOut.V2G_Message.Body.ChargeParameterDiscoveryRes_isUsed != 1u) { 518 | printf("charge_parameter_request: wrong response type\n"); 519 | return -1; 520 | } 521 | // === Validate response code === 522 | if (verify_response_code(res->ResponseCode) != 0) { 523 | printf("charge_parameter_request: authorization response NOT ok, code = %d\n", res->ResponseCode); 524 | return -1; 525 | } 526 | // === Decide which tuple to use === 527 | if (res->SAScheduleList_isUsed && res->SAScheduleList.SAScheduleTuple.arrayLen > 0) { 528 | // === One can implement advanced logic to decide which tuple should be used here === 529 | s->pmax_schedule.is_used = true; 530 | s->pmax_schedule.tupleid = res->SAScheduleList.SAScheduleTuple.array[0].SAScheduleTupleID; 531 | } 532 | // === Print digest === 533 | /*printACEVSEStatus(&(res->AC_EVSEChargeParameter.AC_EVSEStatus)); 534 | printf("\t EVSEProcessing=%d\n", res->EVSEProcessing); 535 | printf("\t EVSEMaxCurrent=%d\n", res->AC_EVSEChargeParameter.EVSEMaxCurrent.Value); 536 | printf("\t EVSENominalVoltage=%d\n", res->AC_EVSEChargeParameter.EVSENominalVoltage.Value);*/ 537 | return 0; 538 | } 539 | 540 | int power_delivery_request(evcc_conn_t *conn, ev_session_t *s, 541 | v2gchargeProgressType progress) 542 | { 543 | int err; 544 | struct v2gEXIDocument exiIn; 545 | struct v2gEXIDocument exiOut; 546 | struct v2gChargingProfileType *profile = &exiIn.V2G_Message.Body.PowerDeliveryReq.ChargingProfile; 547 | 548 | init_v2g_request(&exiIn, s); 549 | exiIn.V2G_Message.Body.PowerDeliveryReq_isUsed = 1u; 550 | init_v2gPowerDeliveryReqType(&exiIn.V2G_Message.Body.PowerDeliveryReq); 551 | 552 | exiIn.V2G_Message.Body.PowerDeliveryReq.DC_EVPowerDeliveryParameter_isUsed = 0; 553 | exiIn.V2G_Message.Body.PowerDeliveryReq.ChargeProgress = progress; 554 | 555 | // === A charging profile is used for this request=== 556 | 557 | exiIn.V2G_Message.Body.PowerDeliveryReq.SAScheduleTupleID = s->pmax_schedule.tupleid; 558 | 559 | if (progress == v2gchargeProgressType_Start) { 560 | exiIn.V2G_Message.Body.PowerDeliveryReq.ChargingProfile_isUsed = 1u; 561 | // Must be initialized to 0 562 | // == Charging Entries == 563 | //SetProfileEntry(profile, relative time, power, max phases) 564 | profile->ProfileEntry.arrayLen = 0; // must be 0 565 | SetProfileEntry(profile, 0, 15000, 3); 566 | SetProfileEntry(profile, 100, 20000, 3); 567 | SetProfileEntry(profile, 200, 10000, 3); 568 | SetProfileEntry(profile, 400, 0, 3); 569 | } else { 570 | exiIn.V2G_Message.Body.PowerDeliveryReq.ChargingProfile_isUsed = 0; 571 | } 572 | err = v2g_request(conn, &exiIn, &exiOut); 573 | if (err != 0) { 574 | printf("power_delivery_request v2g_request error, exiting\n"); 575 | return -1; 576 | } 577 | // === Validate response type === 578 | if (exiOut.V2G_Message.Body.PowerDeliveryRes_isUsed != 1u) { 579 | printf("power_delivery_request: wrong response type\n"); 580 | return -1; 581 | } 582 | // === Validate response code === 583 | if (verify_response_code(exiOut.V2G_Message.Body.PowerDeliveryRes.ResponseCode) != 0) { 584 | printf("power_delivery_request: response NOT ok, code = %d\n", exiOut.V2G_Message.Body.PowerDeliveryRes.ResponseCode); 585 | return -1; 586 | } 587 | //printACEVSEStatus(&(exiOut.V2G_Message.Body.PowerDeliveryRes.AC_EVSEStatus)); 588 | return 0; 589 | } 590 | 591 | int charging_status_request(evcc_conn_t *conn, ev_session_t *s) 592 | { 593 | int err; 594 | struct v2gEXIDocument exiIn; 595 | struct v2gEXIDocument exiOut; 596 | struct v2gChargingStatusResType *res = &exiOut.V2G_Message.Body.ChargingStatusRes; 597 | init_v2g_request(&exiIn, s); 598 | exiIn.V2G_Message.Body.ChargingStatusReq_isUsed = 1u; 599 | init_v2gChargingStatusReqType(&exiIn.V2G_Message.Body.ChargingStatusReq); 600 | err = v2g_request(conn, &exiIn, &exiOut); 601 | if (err != 0) { 602 | printf("harging_status_request: unable to do v2g_request, exiting\n"); 603 | return -1; 604 | } 605 | // === Validate response type === 606 | if (exiOut.V2G_Message.Body.ChargingStatusRes_isUsed != 1u) { 607 | printf("charging_status_request: wrong response type\n"); 608 | return -1; 609 | } 610 | // === Validate response code === 611 | if (verify_response_code(res->ResponseCode) != 0) { 612 | printf("charging_status_request: authorization response NOT ok, code = %d\n", exiOut.V2G_Message.Body.ChargingStatusRes.ResponseCode); 613 | return -1; 614 | } 615 | if (res->AC_EVSEStatus.EVSENotification <= v2gEVSENotificationType_ReNegotiation) { 616 | s->evse_notification = res->AC_EVSEStatus.EVSENotification; 617 | } 618 | return 0; 619 | } 620 | 621 | int session_stop_request(evcc_conn_t *conn, ev_session_t *s) 622 | { 623 | int err; 624 | struct v2gEXIDocument exiIn; 625 | struct v2gEXIDocument exiOut; 626 | init_v2g_request(&exiIn, s); 627 | exiIn.V2G_Message.Body.SessionStopReq_isUsed = 1u; 628 | init_v2gSessionStopReqType(&exiIn.V2G_Message.Body.SessionStopReq); 629 | err = v2g_request(conn, &exiIn, &exiOut); 630 | if (err != 0) { 631 | printf("harging_status_request: unable to do v2g_request, exiting\n"); 632 | return -1; 633 | } 634 | // === Validate response type === 635 | if (exiOut.V2G_Message.Body.SessionStopRes_isUsed != 1u) { 636 | printf("charging_status_request: wrong response type\n"); 637 | return -1; 638 | } 639 | // === Validate response code === 640 | if (verify_response_code(exiOut.V2G_Message.Body.SessionStopRes.ResponseCode) != 0) { 641 | printf("charging_status_request: authorization response NOT ok, code = %d\n", exiOut.V2G_Message.Body.SessionStopRes.ResponseCode); 642 | return -1; 643 | } 644 | return 0; 645 | } 646 | -------------------------------------------------------------------------------- /example/client.h: -------------------------------------------------------------------------------- 1 | #ifndef EXAMPLE_CLIENT_H 2 | #define EXAMPLE_CLIENT_H 3 | typedef struct ev_session ev_session_t; 4 | struct ev_session{ 5 | uint64_t id; 6 | uint16_t charge_service_id; 7 | bool charging_is_free; 8 | v2gEVSENotificationType evse_notification; 9 | uint8_t challenge[16]; 10 | struct{ 11 | bool is_used; 12 | uint8_t tupleid; 13 | } pmax_schedule; 14 | struct{ 15 | uint8_t cert[v2gCertificateChainType_Certificate_BYTES_SIZE]; 16 | size_t cert_len; 17 | uint8_t sub_certs[v2gSubCertificatesType_Certificate_ARRAY_SIZE][v2gCertificateChainType_Certificate_BYTES_SIZE]; 18 | size_t subcert_len[v2gSubCertificatesType_Certificate_ARRAY_SIZE]; 19 | ecdsa_context key; 20 | entropy_context entropy; 21 | ctr_drbg_context ctr_drbg; 22 | } contract; 23 | }; 24 | void init_v2g_request(struct v2gEXIDocument *exiIn, ev_session_t *s); 25 | void SetProfileEntry(struct v2gChargingProfileType* prof, 26 | uint32_t start, int32_t value, 27 | uint32_t nphases); 28 | int verify_response_code(v2gresponseCodeType code); 29 | void evcc_session_cleanup(ev_session_t* s); 30 | int load_contract(const char *pemchain_path, const char *keyfile_path, ev_session_t *s); 31 | int sign_auth_request(struct v2gAuthorizationReqType *req, 32 | ecdsa_context *key, 33 | ctr_drbg_context *ctr_drbg, 34 | struct v2gSignatureType *sig); 35 | int session_request(evcc_conn_t *conn, ev_session_t *s); 36 | int service_discovery_request(evcc_conn_t *conn, ev_session_t *s); 37 | int payment_selection_request(evcc_conn_t *conn, ev_session_t *s); 38 | int payment_details_request(evcc_conn_t *conn, ev_session_t *s); 39 | int authorization_request(evcc_conn_t *conn, ev_session_t *s); 40 | int charge_parameter_request(evcc_conn_t *conn, ev_session_t *s); 41 | int power_delivery_request(evcc_conn_t *conn, ev_session_t *s, v2gchargeProgressType progress); 42 | int charging_status_request(evcc_conn_t *conn, ev_session_t *s); 43 | int session_stop_request(evcc_conn_t *conn, ev_session_t *s); 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /example/server.h: -------------------------------------------------------------------------------- 1 | #ifndef SERVER_H 2 | #define SERVER_H 3 | extern int secc_free_charge; 4 | #include 5 | #include 6 | 7 | typedef struct session_data session_data_t; 8 | struct session_data { 9 | uint8_t evcc_id[6]; // EV mac address 10 | struct v2gSelectedServiceType services[v2gSelectedServiceListType_SelectedService_ARRAY_SIZE]; 11 | v2gEnergyTransferModeType energy_transfer_mode; 12 | v2gpaymentOptionType payment_type; 13 | struct v2gSAScheduleListType SAScheduleList; 14 | bool SAScheduleList_isUsed; 15 | uint8_t challenge[16]; 16 | bool verified; 17 | bool charging; 18 | bool tls_enabled; 19 | bool renegotiation_required; 20 | struct{ 21 | bool valid_crt; // Before a contract can be valid, it must have a valid crt 22 | //byte cert[v2gCertificateChainType_Certificate_BYTES_SIZE]; 23 | //size_t cert_len; 24 | x509_crt crt; 25 | ecdsa_context pubkey; 26 | } contract; 27 | }; 28 | void session_data_cleanup(session_t *s); 29 | void init_v2g_response(struct v2gEXIDocument *exiOut, session_t *s); 30 | int verify_charging_profile(session_data_t *sd, uint8_t tupleid, struct v2gChargingProfileType *profile); 31 | 32 | extern x509_crt Trusted_contract_rootcert_chain; 33 | 34 | int create_response_message(struct v2gEXIDocument *exiIn, struct v2gEXIDocument *exiOut, bool tls_enabled); 35 | 36 | 37 | int handle_session_setup(struct v2gEXIDocument *exiIn, 38 | struct v2gEXIDocument *exiOut, 39 | session_t *s, session_data_t *sd); 40 | int handle_service_discovery(struct v2gEXIDocument *exiIn, 41 | struct v2gEXIDocument *exiOut, 42 | session_t *s, session_data_t *sd); 43 | int payment_service_selection(struct v2gEXIDocument *exiIn, 44 | struct v2gEXIDocument *exiOut, 45 | session_t *s, session_data_t *sd); 46 | int handle_service_detail(struct v2gEXIDocument *exiIn, 47 | struct v2gEXIDocument *exiOut, 48 | session_t *s, session_data_t *sd); 49 | int handle_payment_detail(struct v2gEXIDocument *exiIn, 50 | struct v2gEXIDocument *exiOut, 51 | session_t *s, session_data_t *sd); 52 | int handle_authorization(struct v2gEXIDocument *exiIn, 53 | struct v2gEXIDocument *exiOut, 54 | session_t *s, session_data_t *sd); 55 | int handle_charge_parameters(struct v2gEXIDocument *exiIn, 56 | struct v2gEXIDocument *exiOut, 57 | session_t *s, session_data_t *sd); 58 | int handle_power_delivery(struct v2gEXIDocument *exiIn, 59 | struct v2gEXIDocument *exiOut, 60 | session_t *s, session_data_t *sd); 61 | int handle_charging_status(struct v2gEXIDocument *exiIn, 62 | struct v2gEXIDocument *exiOut, 63 | session_t *s, session_data_t *sd); 64 | int handle_session_stop(struct v2gEXIDocument *exiIn, 65 | struct v2gEXIDocument *exiOut, 66 | session_t *s, session_data_t *sd); 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /example/slac/homeplug.h: -------------------------------------------------------------------------------- 1 | /*====================================================================* 2 | * 3 | * Copyright (c) 2013 Qualcomm Atheros, Inc. 4 | * 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or 8 | * without modification, are permitted (subject to the limitations 9 | * in the disclaimer below) provided that the following conditions 10 | * are met: 11 | * 12 | * * Redistributions of source code must retain the above copyright 13 | * notice, this list of conditions and the following disclaimer. 14 | * 15 | * * Redistributions in binary form must reproduce the above 16 | * copyright notice, this list of conditions and the following 17 | * disclaimer in the documentation and/or other materials 18 | * provided with the distribution. 19 | * 20 | * * Neither the name of Qualcomm Atheros nor the names of 21 | * its contributors may be used to endorse or promote products 22 | * derived from this software without specific prior written 23 | * permission. 24 | * 25 | * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE 26 | * GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE 27 | * COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 28 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 29 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 31 | * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 32 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 33 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 34 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 35 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 36 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 37 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 38 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 39 | * 40 | *--------------------------------------------------------------------*/ 41 | 42 | /*====================================================================* 43 | * 44 | * homeplug.h - HomePlug Definitions and Declarations; 45 | * 46 | *. Qualcomm Atheros HomePlug AV Powerline Toolkit 47 | *: Copyright (c) 2009-2013 by Qualcomm Atheros Inc. ALL RIGHTS RESERVED; 48 | *; For demonstration and evaluation only; Not for production use. 49 | * 50 | * Contributor(s): 51 | * Charles Maier 52 | * Alex Vasquez 53 | * 54 | *--------------------------------------------------------------------*/ 55 | 56 | #ifndef HOMEPLUG_HEADER 57 | #define HOMEPLUG_HEADER 58 | 59 | /*====================================================================* 60 | * HomePlug AV Constants; 61 | *--------------------------------------------------------------------*/ 62 | 63 | #define HOMEPLUG_MMV 0x01 64 | #define HOMEPLUG_MMTYPE 0x0000 65 | 66 | /*====================================================================* 67 | * HomePlug Management Message Ranges for Information Only; 68 | *--------------------------------------------------------------------*/ 69 | 70 | #define CC_MMTYPE_MIN 0x0000 71 | #define CC_MMTYPE_MAX 0x1FFF 72 | #define CP_MMTYPE_MIN 0x2000 73 | #define CP_MMTYPE_MAX 0x3FFF 74 | #define NN_MMTYPE_MIN 0x4000 75 | #define NN_MMTYPE_MAX 0x5FFF 76 | #define CM_MMTYPE_MIN 0x6000 77 | #define CM_MMTYPE_MAX 0x7FFF 78 | #define MS_MMTYPE_MIN 0x8000 79 | #define MS_MMTYPE_MAX 0x9FFF 80 | #define VS_MMTYPE_MIN 0xA000 81 | #define VS_MMTYPE_MAX 0xBFFF 82 | #define HA_MMTYPE_MIN 0xC000 83 | #define HA_MMTYPE_MAX 0xFFFF 84 | 85 | /*====================================================================* 86 | * HomePlug AV MMEs have 4 variants indicated by the 2 MMTYPE LSBs; 87 | *--------------------------------------------------------------------*/ 88 | 89 | #define MMTYPE_CC 0x0000u 90 | #define MMTYPE_CP 0x2000u 91 | #define MMTYPE_NN 0x4000u 92 | #define MMTYPE_CM 0x6000u 93 | #define MMTYPE_MS 0x8000u 94 | #define MMTYPE_VS 0xA000u 95 | #define MMTYPE_XX 0xC000u 96 | 97 | #ifndef IHPAPI_HEADER 98 | #define MMTYPE_REQ 0x0000u 99 | #define MMTYPE_CNF 0x0001u 100 | #define MMTYPE_IND 0x0002u 101 | #define MMTYPE_RSP 0x0003u 102 | #define MMTYPE_MODE (MMTYPE_REQ|MMTYPE_CNF|MMTYPE_IND|MMTYPE_RSP) 103 | #define MMTYPE_MASK ~(MMTYPE_REQ|MMTYPE_CNF|MMTYPE_IND|MMTYPE_RSP) 104 | #endif 105 | 106 | /*====================================================================* 107 | * HomePlug AV Management Message Types; 108 | *--------------------------------------------------------------------*/ 109 | 110 | #define CC_CCO_APPOINT 0x0000u 111 | #define CC_BACKUP_APPOINT 0x0004u 112 | #define CC_LINK_INFO 0x0008u 113 | #define CC_HANDOVER 0x000Cu 114 | #define CC_HANDOVER_INFO 0x0010u 115 | #define CC_DISCOVER_LIST 0x0014u 116 | #define CC_LINK_NEW 0x0018u 117 | #define CC_LINK_MOD 0x001Cu 118 | #define CC_LINK_SQZ 0x0020u 119 | #define CC_LINK_REL 0x0024u 120 | #define CC_DETECT_REPORT 0x0028u 121 | #define CC_WHO_RU 0x002Cu 122 | #define CC_ASSOC 0x0030u 123 | #define CC_LEAVE 0x0034u 124 | #define CC_SET_TEI_MAP 0x0038u 125 | #define CC_RELAY 0x003Cu 126 | #define CC_BEACON_RELIABILITY 0x0040u 127 | #define CC_ALLOC_MOVE 0x0044u 128 | #define CC_ACCESS_NEW 0x0048u 129 | #define CC_ACCESS_REL 0x004Cu 130 | #define CC_DCPPC 0x0050u 131 | #define CC_HP1_DET 0x0054u 132 | #define CC_BLE_UPDATE 0x0058u 133 | #define CP_PROXY_APPOINT 0x2000u 134 | #define PH_PROXY_APPOINT 0x2004u 135 | #define CP_PROXY_WAKE 0x2008u 136 | #define NN_INL 0x4000u 137 | #define NN_NEW_NET 0x4004u 138 | #define NN_ADD_ALLOC 0x4008u 139 | #define NN_REL_ALLOC 0x400Cu 140 | #define NN_REL_NET 0x4010u 141 | #define CM_ASSOCIATED_STA 0x6000u 142 | #define CM_ENCRYPTED_PAYLOAD 0x6004u 143 | #define CM_SET_KEY 0x6008u 144 | #define CM_GET_KEY 0x600Cu 145 | #define CM_SC_JOIN 0x6010u 146 | #define CM_CHAN_EST 0x6014u 147 | #define CM_TM_UPDATE 0x6018u 148 | #define CM_AMP_MAP 0x601Cu 149 | #define CM_BRG_INFO 0x6020u 150 | #define CM_CONN_NEW 0x6024u 151 | #define CM_CONN_REL 0x6028u 152 | #define CM_CONN_MOD 0x602Cu 153 | #define CM_CONN_INFO 0x6030u 154 | #define CM_STA_CAP 0x6034u 155 | #define CM_NW_INFO 0x6038u 156 | #define CM_GET_BEACON 0x603Cu 157 | #define CM_HFID 0x6040u 158 | #define CM_MME_ERROR 0x6044u 159 | #define CM_NW_STATS 0x6048u 160 | #define CM_SLAC_PARAM 0x6064u 161 | #define CM_START_ATTEN_CHAR 0x6068u 162 | #define CM_ATTEN_CHAR 0x606Cu 163 | #define CM_PKCS_CERT 0x6070u 164 | #define CM_MNBC_SOUND 0x6074u 165 | #define CM_VALIDATE 0x6078u 166 | #define CM_SLAC_MATCH 0x607Cu 167 | #define CM_SLAC_USER_DATA 0x6080u 168 | #define CM_ATTEN_PROFILE 0x6084u 169 | 170 | /*====================================================================* 171 | * 172 | *--------------------------------------------------------------------*/ 173 | 174 | #endif 175 | -------------------------------------------------------------------------------- /example/slac/plc_eth.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "plc_eth.h" 3 | 4 | void print_byte_arr(void *varr, size_t n) 5 | { 6 | uint8_t *arr = (uint8_t*)varr; 7 | int i; 8 | if (chattyv2g) fprintf(stderr, "["); 9 | // Highly ineffictive but whatever it's TESTING!! :D 10 | for (i = 0; i < n; i++) { 11 | if (chattyv2g) fprintf(stderr, " %02x", arr[i]); 12 | } 13 | if (chattyv2g) fprintf(stderr, " ]\n"); 14 | } 15 | 16 | static int lookup_mac(const char *if_name, uint8_t *mac) 17 | { 18 | char buf[64 + IFNAMSIZ]; 19 | FILE *f; 20 | if (strlen(if_name) > IFNAMSIZ) { return -1; } 21 | 22 | sprintf(buf, "/sys/class/net/%s/address", if_name); 23 | if ((f = fopen(buf, "r")) == NULL) { return -1; } 24 | if (fscanf(f, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", 25 | mac,mac+1,mac+2,mac+3,mac+4,mac+5) != 6) { 26 | fclose(f); 27 | return -1; 28 | } 29 | fclose(f); 30 | return 0; 31 | } 32 | 33 | int ethdial(ethconn_t *ethconn, const char *if_name, uint16_t protocol) 34 | { 35 | uint8_t src_mac[6]; 36 | unsigned idx; 37 | 38 | if ((idx = if_nametoindex(if_name)) == 0) { return -1; } 39 | if (lookup_mac(if_name, src_mac) != 0) { 40 | errno = ENXIO; 41 | return -1; 42 | } 43 | memcpy(ethconn->src_mac, src_mac, sizeof(src_mac)); 44 | ethconn->protocol = protocol; 45 | ethconn->if_index = idx; 46 | ethconn->sockfd = socket(AF_PACKET, SOCK_RAW, htons(protocol)); 47 | if (ethconn->sockfd < 0) { return -1; } 48 | return 0; 49 | } 50 | 51 | int ethclose(ethconn_t *ethconn) 52 | { 53 | return close(ethconn->sockfd); 54 | } 55 | 56 | ssize_t ethrecvfrom(ethconn_t *ethconn, void *buffer, const uint8_t remote_mac[ETH_ALEN]) 57 | { 58 | struct sockaddr_ll rsa = { 59 | .sll_family = AF_PACKET, 60 | .sll_protocol = htons(ETH_P_IP), 61 | .sll_ifindex = ethconn->if_index, 62 | .sll_hatype = ARPHRD_ETHER, 63 | .sll_pkttype = PACKET_OTHERHOST, 64 | .sll_halen = ETH_ALEN 65 | }; 66 | socklen_t sockaddr_len = sizeof(rsa); 67 | memcpy(rsa.sll_addr, remote_mac, ETH_ALEN); 68 | return recvfrom(ethconn->sockfd, buffer, ETH_FRAME_LEN, 0, 69 | (struct sockaddr *)&rsa, &sockaddr_len); 70 | } 71 | 72 | ssize_t ethrecv(ethconn_t *ethconn, void *buffer) 73 | { 74 | return recvfrom(ethconn->sockfd, buffer, ETH_FRAME_LEN, 0, NULL, NULL); 75 | } 76 | 77 | void ethwritehdr(void *vbuf, ethconn_t *ethconn, const uint8_t destmac[6]) 78 | { 79 | uint8_t *buf = vbuf; 80 | memcpy(buf, destmac, ETH_ALEN); 81 | memcpy(buf + ETH_ALEN, ethconn->src_mac, ETH_ALEN); 82 | //eh->h_proto = 0xcafe; 83 | buf[12] = ethconn->protocol >> 8; 84 | buf[13] = ethconn->protocol & 0xff; 85 | } 86 | 87 | int ethsend(ethconn_t *ethconn, const void *data, size_t len) 88 | { 89 | struct sockaddr_ll rsa = { 90 | .sll_family = AF_PACKET, 91 | .sll_protocol = htons(ETH_P_IP), 92 | .sll_ifindex = ethconn->if_index, 93 | .sll_hatype = ARPHRD_ETHER, 94 | .sll_pkttype = PACKET_OTHERHOST, 95 | .sll_halen = ETH_ALEN 96 | }; 97 | uint8_t mindata[ETH_FRAME_MIN_SIZE]; 98 | 99 | if (len > ETH_FRAME_LEN) { 100 | errno = EMSGSIZE; 101 | return -1; 102 | } 103 | if (len < ETH_FRAME_MIN_SIZE) { 104 | memset(mindata, 0, sizeof(mindata)); 105 | memcpy(mindata, data, len); 106 | data = mindata; 107 | len = ETH_FRAME_MIN_SIZE; 108 | } 109 | memcpy(rsa.sll_addr, data, ETH_ALEN); /* XXX does this matter? */ 110 | if (sendto(ethconn->sockfd, data, len, 0, 111 | (struct sockaddr *)&rsa, sizeof(rsa)) < 0) { 112 | return -1; 113 | } 114 | return 0; 115 | } 116 | 117 | 118 | 119 | //================================== 120 | // Higher Level Functions 121 | //================================== 122 | 123 | int toggle_listen_slac_assn(ethconn_t *ethconn, const uint8_t dest_mac[6], bool listen) 124 | { 125 | static const uint8_t listen_payload[3] = { 0x00, 0x0b, 0x01 }; 126 | static const uint8_t stoplisten_payload[4] = { 0x00, 0x0b, 0x03, 0x00 }; 127 | uint8_t ethframe[ETH_FRAME_LEN]; 128 | uint8_t *payload = ethframe + ETH_FRAME_HDR_SIZE; 129 | size_t payload_len; 130 | memset(ethframe, 0, sizeof(ethframe)); 131 | ethwritehdr(ethframe, ethconn, dest_mac); 132 | if (listen) { 133 | memcpy(payload, listen_payload, sizeof(listen_payload)); 134 | payload_len = 61; 135 | } else { 136 | memcpy(payload, stoplisten_payload, sizeof(stoplisten_payload)); 137 | payload_len = 4; 138 | } 139 | return ethsend(ethconn, ethframe, ETH_FRAME_HDR_SIZE + payload_len); 140 | } 141 | 142 | //int terminate_dlink(ethconn_t *ethconn, byte dest_mac[6], bool resetup) 143 | //{ 144 | // byte ethframe[ETH_FRAME_LEN]; 145 | // byte *payload = ethframe + ETH_FRAME_HDR_SIZE; 146 | // size_t payload_len; 147 | // byte payload[62]; 148 | // int err; 149 | // payload[0] = 0x0b; 150 | // payload[1] = 0x07; 151 | // payload[2] = !resetup; 152 | // memset(payload+3, 0, 58); 153 | // err = ethsend(ethconn, dest_mac, payload, 61); 154 | // if (err == -1) { 155 | // if (chattyv2g) fprintf(stderr, "%s: %m\n", "plc_eth_send"); 156 | // } 157 | // return err; 158 | //} 159 | 160 | int switch_power_line(const char *if_name, const uint8_t dest_mac[6], bool toggle_powerline) 161 | { 162 | static const uint8_t powerline_payload[3] = { 0x00, 0x0f, 0x10 }; 163 | static const uint8_t pilotline_payload[3] = { 0x00, 0x0f, 0x0e }; 164 | uint8_t ethframe[ETH_FRAME_LEN]; 165 | uint8_t *payload = ethframe + ETH_FRAME_HDR_SIZE; 166 | size_t payload_len; 167 | ethconn_t ethconn; 168 | int r, e; 169 | 170 | if (ethdial(ðconn, if_name, 0xcafe) != 0) { return -1; } 171 | 172 | memset(ethframe, 0, sizeof(ethframe)); 173 | ethwritehdr(ethframe, ðconn, dest_mac); 174 | if (toggle_powerline) { 175 | memcpy(payload, powerline_payload, 3); 176 | payload_len = 61; 177 | } else { 178 | memcpy(payload, pilotline_payload, 3); 179 | payload_len = 61; 180 | } 181 | r = ethsend(ðconn, ethframe, ETH_FRAME_HDR_SIZE + payload_len); 182 | 183 | e = errno; 184 | ethclose(ðconn); 185 | errno = e; 186 | 187 | return r; 188 | } 189 | 190 | uint16_t get_slac_type(const void *buf) 191 | { 192 | const uint8_t *_buf = buf; 193 | return ((uint16_t)_buf[ETH_FRAME_HDR_SIZE + 1] << 8) | 194 | (uint16_t)_buf[ETH_FRAME_HDR_SIZE + 2]; 195 | } 196 | -------------------------------------------------------------------------------- /example/slac/plc_eth.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | // Arm tasks 15 | // zhasha: http://sprunge.us/NbFA task-arm.s 16 | // http://sprunge.us/aahI task-386.s 17 | 18 | // Powerline GP Reference: 19 | //http://www.insys-icom.com/bausteine.net/f/10637/HB_en_INSYS_Powerline_GP_1401.pdf?fd=0#page=36 20 | 21 | #ifndef PLC_ETH_H 22 | #define PLC_ETH_H 1 23 | enum { 24 | ETH_FRAME_HDR_SIZE = 14, 25 | ETH_FRAME_MIN_PAYLOAD_SIZE = 46, 26 | ETH_FRAME_MIN_SIZE = ETH_FRAME_HDR_SIZE + ETH_FRAME_MIN_PAYLOAD_SIZE, 27 | }; 28 | 29 | static uint8_t ETH_BROADCAST_ADDR[ETH_ALEN] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; 30 | static uint8_t ETH_LOCAL_ATHEROS_DEVICE[ETH_ALEN] = {0x00, 0xb0, 0x52, 0x00, 0x00, 0x01}; 31 | 32 | #define ETH_P_HPAV 0x88E1 33 | #define ETH_P_TOGGLE_PILOT 0xcafe 34 | #define ETH_P_SLAC 0xabba 35 | 36 | #define SLAC_RES_DLINK_READY 0x0b06 37 | 38 | 39 | typedef struct ethconn_t ethconn_t; 40 | struct ethconn_t{ 41 | int sockfd; 42 | int if_index; 43 | uint8_t src_mac[6]; 44 | // byte dest_mac[6]; 45 | uint16_t protocol; 46 | // struct sockaddr_ll rsaddr; 47 | }; 48 | //=======================================0 49 | // Note that ALL receiving buffers must 50 | // be at least ETH_FRAME_LEN bytes long 51 | int ethdial(ethconn_t *eth_conn, const char *if_name, uint16_t protocol); 52 | int ethclose(ethconn_t *eth_conn); 53 | 54 | ssize_t ethrecv(ethconn_t *eth_conn, void *buffer); 55 | ssize_t ethrecvfrom(ethconn_t *eth_conn, void *buffer, const uint8_t remote_mac[ETH_ALEN]); 56 | int ethsend(ethconn_t *ethconn, const void *data, size_t len); 57 | 58 | void ethwritehdr(void *vbuf, ethconn_t *ethconn, const uint8_t destmac[6]); 59 | 60 | //===== HIGHER LEVEL STUFF ====== 61 | 62 | int switch_power_line(const char *if_name, const uint8_t dest_mac[6], bool powerline); 63 | int toggle_listen_slac_assn(ethconn_t *ethconn, const uint8_t dest_mac[6], bool listen); 64 | 65 | //int terminate_dlink(struct ethconn_t *ethconn,byte dest_mac[6], bool resetup); 66 | 67 | uint16_t get_slac_type(const void *buf); 68 | 69 | void print_byte_arr(void *arr, size_t n); 70 | #endif 71 | -------------------------------------------------------------------------------- /example/slac/powerlinegp_evse_slac.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "plc_eth.h" 5 | 6 | typedef uint8_t byte; 7 | 8 | typedef struct plgp_slac_args plgp_slac_args_t; 9 | struct plgp_slac_args{ 10 | const char *if_name; 11 | byte *evse_mac; 12 | }; 13 | 14 | static void dumpbytes(const void *data, size_t len) 15 | { 16 | const byte *d = data; 17 | size_t i, j; 18 | 19 | if (!chattyv2g) { return; } 20 | 21 | for (i = 0; i < len; i += 16) { 22 | fprintf(stderr, " "); 23 | for (j = 0; j < 16 && i + j < len; ++j) { 24 | fprintf(stderr, " %02X", d[i + j]); 25 | } 26 | fprintf(stderr, "\n"); 27 | } 28 | } 29 | 30 | void plgp_slac_listen_blocking(void *vargs) 31 | { 32 | plgp_slac_args_t *args = (plgp_slac_args_t*)vargs; 33 | ethconn_t ethconn; 34 | byte buffer[ETH_FRAME_LEN]; 35 | int err; 36 | ssize_t n; 37 | char if_name[IFNAMSIZ]; 38 | byte dest_mac_evse[6]; 39 | //Chan c1; 40 | strcpy(if_name, args->if_name); 41 | memcpy(dest_mac_evse, args->evse_mac, 6); 42 | rendez(vargs, NULL); 43 | err = ethdial(ðconn, if_name, ETH_P_SLAC); 44 | if (err != 0) { 45 | perror("dial"); 46 | exit(1); 47 | } 48 | toggle_listen_slac_assn(ðconn, dest_mac_evse, false); 49 | toggle_listen_slac_assn(ðconn, dest_mac_evse, true); 50 | printf("Starting SLAC listener!\n"); 51 | while (1) { 52 | n = ethrecv(ðconn, buffer); 53 | uint16_t slac_type = get_slac_type(buffer); 54 | //if (slac_type == SLAC_RES_DLINK_READY && buffer[17] == 0x01) { 55 | if (slac_type == 0xb09 && buffer[17] != 0x01) { 56 | toggle_listen_slac_assn(ðconn, dest_mac_evse, false); 57 | toggle_listen_slac_assn(ðconn, dest_mac_evse, true); 58 | //terminate_dlink(ðconn, true); 59 | } else { 60 | switch (slac_type) { 61 | case 0x0b02: 62 | if (buffer[17] != 0x01) { 63 | dumpbytes(buffer, n); 64 | } else { 65 | printf("."); 66 | fflush(stdout); 67 | } 68 | break; 69 | case 0x0b04: 70 | if (buffer[17] != 0x01) { 71 | dumpbytes(buffer, n); 72 | } 73 | break; 74 | case 0x0b06: 75 | if (buffer[17] != 0x01) { 76 | printf("Data Link Established!\n"); 77 | 78 | } 79 | break; 80 | case 0x0b05: 81 | printf("Attenuation profile received\n"); 82 | //dumpbytes(buffer, n); 83 | break; 84 | default: 85 | printf("unexpected slac type %0x\n", slac_type); 86 | dumpbytes(buffer, n); 87 | break; 88 | } 89 | } 90 | } 91 | } 92 | int plgp_slac_listen(const char *if_name, byte dest_mac_evse[6]) 93 | { 94 | plgp_slac_args_t args = { 95 | .if_name = if_name, 96 | .evse_mac = dest_mac_evse 97 | }; 98 | if (threadcreate(plgp_slac_listen_blocking, &args, 1024 * 1024) < 0) { 99 | return -1; 100 | } 101 | rendez(&args, NULL); 102 | return 0; 103 | } 104 | -------------------------------------------------------------------------------- /example/slac/qualcomm_slac.h: -------------------------------------------------------------------------------- 1 | /*====================================================================* 2 | * 3 | * Copyright (c) 2013 Qualcomm Atheros, Inc. 4 | * 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or 8 | * without modification, are permitted (subject to the limitations 9 | * in the disclaimer below) provided that the following conditions 10 | * are met: 11 | * 12 | * * Redistributions of source code must retain the above copyright 13 | * notice, this list of conditions and the following disclaimer. 14 | * 15 | * * Redistributions in binary form must reproduce the above 16 | * copyright notice, this list of conditions and the following 17 | * disclaimer in the documentation and/or other materials 18 | * provided with the distribution. 19 | * 20 | * * Neither the name of Qualcomm Atheros nor the names of 21 | * its contributors may be used to endorse or promote products 22 | * derived from this software without specific prior written 23 | * permission. 24 | * 25 | * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE 26 | * GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE 27 | * COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 28 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 29 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 31 | * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 32 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 33 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 34 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 35 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 36 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 37 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 38 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 39 | * 40 | *--------------------------------------------------------------------*/ 41 | 42 | /*====================================================================* 43 | * 44 | * slac.h - 45 | * 46 | * this file contains defintions for the HomePlug Green PHY SLAC 47 | * protocol; 48 | * 49 | *--------------------------------------------------------------------*/ 50 | 51 | #ifndef SLAC_HEADER 52 | #define SLAC_HEADER 53 | 54 | /*====================================================================* 55 | * system header files; 56 | *--------------------------------------------------------------------*/ 57 | 58 | #include 59 | 60 | /*====================================================================* 61 | * custom header files; 62 | *--------------------------------------------------------------------*/ 63 | #include 64 | //#include "../tools/types.h" 65 | //#include "../tools/endian.h" 66 | //#include "../ether/ether.h" 67 | //#include "../mme/mme.h" 68 | 69 | /*====================================================================* 70 | * constants; 71 | *--------------------------------------------------------------------*/ 72 | 73 | #define SLAC_DEBUG 1 74 | 75 | /* 76 | * The following two constants control whether or not the PEV or EVSE 77 | * change AVLN on SLAC protocol cycle; The recommended setting is PEV 78 | * changes with each pass and the EVSE does not; 79 | */ 80 | 81 | #define SLAC_AVLN_PEV 1 82 | #define SLAC_AVLN_EVSE 0 83 | 84 | #define SLAC_APPLICATION_PEV_EVSE 0x00 85 | 86 | #define SLAC_SECURITY_NONE 0x00 87 | #define SLAC_SECURITY_PUBLIC_KEY 0x01 88 | 89 | #define SLAC_RUNID_LEN 8 90 | #define SLAC_UNIQUE_ID_LEN 17 91 | #define SLAC_RND_LEN 16 92 | #define SLAC_NID_LEN 7 93 | #define SLAC_NMK_LEN 16 94 | 95 | #define SLAC_MSOUNDS 8 96 | #define SLAC_TIMETOSOUND 8 97 | #define SLAC_TIMEOUT 1000 98 | #define SLAC_APPLICATION_TYPE 0 99 | #define SLAC_SECURITY_TYPE 0 100 | #define SLAC_RESPONSE_TYPE 0 101 | #define SLAC_MSOUND_TARGET "FF:FF:FF:FF:FF:FF" 102 | #define SLAC_FORWARD_STATION "00:00:00:00:00:00" 103 | #define SLAC_GROUPS 58 104 | 105 | #define SLAC_LIMIT 40 106 | #define SLAC_PAUSE 20 107 | #define SLAC_SETTLETIME 10 108 | #define SLAC_CHARGETIME 2 109 | #define SLAC_FLAGS 0 110 | 111 | #define SLAC_SILENCE (1 << 0) 112 | #define SLAC_VERBOSE (1 << 1) 113 | #define SLAC_SESSION (1 << 2) 114 | #define SLAC_COMPARE (1 << 3) 115 | 116 | #define SLAC_CM_SETKEY_KEYTYPE 0x01 117 | #define SLAC_CM_SETKEY_PID 0x02 118 | #define SLAC_CM_SETKEY_PRN 0x00 119 | #define SLAC_CM_SETKEY_PMN 0x00 120 | #define SLAC_CM_SETKEY_CCO 0x00 121 | #define SLAC_CM_SETKEY_EKS 0x01 122 | 123 | /*====================================================================* 124 | * messages; 125 | *--------------------------------------------------------------------*/ 126 | 127 | #ifndef __GNUC__ 128 | #pragma pack (push,1) 129 | #endif 130 | 131 | struct __attribute__((packed)) ethernet_hdr 132 | 133 | { 134 | uint8_t ODA [ETHER_ADDR_LEN]; 135 | uint8_t OSA [ETHER_ADDR_LEN]; 136 | uint16_t MTYPE; 137 | }; 138 | 139 | struct __attribute__((packed)) homeplug_hdr 140 | 141 | { 142 | uint8_t MMV; 143 | uint16_t MMTYPE; 144 | }; 145 | 146 | struct __attribute__((packed)) homeplug_fmi 147 | 148 | { 149 | uint8_t MMV; 150 | uint16_t MMTYPE; 151 | uint8_t FMSN; 152 | uint8_t FMID; 153 | }; 154 | 155 | struct __attribute__((packed)) homeplug 156 | { 157 | struct ethernet_hdr ethernet; 158 | struct homeplug_fmi homeplug; 159 | uint8_t content [ETHERMTU - sizeof (struct homeplug_fmi)]; 160 | }; 161 | struct __attribute__((packed)) slac_session 162 | 163 | { 164 | struct session * next; 165 | struct session * prev; 166 | uint8_t RunID [SLAC_RUNID_LEN]; 167 | uint8_t APPLICATION_TYPE; 168 | uint8_t SECURITY_TYPE; 169 | uint8_t RESP_TYPE; 170 | uint8_t NUM_SOUNDS; 171 | uint8_t TIME_OUT; 172 | uint8_t AAG [SLAC_GROUPS]; 173 | uint8_t NumGroups; 174 | uint8_t MSOUND_TARGET [ETHER_ADDR_LEN]; 175 | uint8_t FORWARDING_STA [ETHER_ADDR_LEN]; 176 | uint8_t PEV_ID [SLAC_UNIQUE_ID_LEN]; 177 | //uint8_t PEV_MAC [ETHER_ADDR_LEN]; 178 | uint8_t EVSE_ID [SLAC_UNIQUE_ID_LEN]; 179 | uint8_t EVSE_MAC [ETHER_ADDR_LEN]; 180 | uint8_t RND [SLAC_UNIQUE_ID_LEN]; 181 | uint8_t NMK [SLAC_NMK_LEN]; 182 | uint8_t NID [SLAC_NID_LEN]; 183 | uint8_t original_nmk [SLAC_NMK_LEN]; 184 | uint8_t original_nid [SLAC_NID_LEN]; 185 | unsigned state; 186 | unsigned sounds; 187 | unsigned limit; 188 | unsigned pause; 189 | //unsigned chargetime; 190 | //unsigned settletime; 191 | //unsigned counter; 192 | //unsigned flags; 193 | signed exit; 194 | }; 195 | struct __attribute__((packed)) qualcomm_fmi 196 | 197 | { 198 | uint8_t MMV; 199 | uint16_t MMTYPE; 200 | uint8_t FMSN; 201 | uint8_t FMID; 202 | uint8_t OUI [ETHER_ADDR_LEN >> 1]; 203 | }; 204 | struct __attribute__((packed)) qualcomm_hdr { 205 | uint8_t MMV; 206 | uint16_t MMTYPE; 207 | uint8_t OUI [ETHER_ADDR_LEN >> 1]; 208 | }; 209 | struct __attribute__((packed)) qualcomm 210 | { 211 | struct ethernet_hdr ethernet; 212 | struct qualcomm_fmi qualcomm; 213 | uint8_t content [ETHERMTU - sizeof (struct qualcomm_fmi)]; 214 | }; 215 | 216 | struct __attribute__((packed)) cm_set_key_request { 217 | struct ethernet_hdr ethernet; 218 | struct homeplug_fmi homeplug; 219 | uint8_t KEYTYPE; 220 | uint32_t MYNOUNCE; 221 | uint32_t YOURNOUNCE; 222 | uint8_t PID; 223 | uint16_t PRN; 224 | uint8_t PMN; 225 | uint8_t CCOCAP; 226 | uint8_t NID [SLAC_NID_LEN]; 227 | uint8_t NEWEKS; 228 | uint8_t NEWKEY [SLAC_NMK_LEN]; 229 | uint8_t RSVD [3]; 230 | }; 231 | struct __attribute__((packed)) cm_set_key_confirm { 232 | struct ethernet_hdr ethernet; 233 | struct homeplug_fmi homeplug; 234 | uint8_t RESULT; 235 | uint32_t MYNOUNCE; 236 | uint32_t YOURNOUNCE; 237 | uint8_t PID; 238 | uint16_t PRN; 239 | uint8_t PMN; 240 | uint8_t CCOCAP; 241 | uint8_t RSVD [27]; 242 | }; 243 | 244 | struct __attribute__((packed)) cm_sta_identity_request 245 | 246 | { 247 | struct ethernet_hdr ethernet; 248 | struct homeplug_fmi homeplug; 249 | }; 250 | 251 | struct __attribute__((packed)) cm_sta_identity_confirm 252 | 253 | { 254 | struct ethernet_hdr ethernet; 255 | struct homeplug_fmi homeplug; 256 | uint8_t GREEN_PHY_CAPABILITY; 257 | uint8_t POWER_SAVE_CAPABILITY; 258 | uint8_t GREEN_PHY_PREFERRED_ALLOCATION_CAPABILITY; 259 | uint8_t REPEATING_AND_ROUTING_CAPABILITY; 260 | uint8_t HOMEPLUG_AV_VERSION; 261 | uint8_t EFL; 262 | uint8_t EF [1]; 263 | }; 264 | 265 | struct __attribute__((packed)) cm_slac_param_request 266 | 267 | { 268 | struct ethernet_hdr ethernet; 269 | struct homeplug_fmi homeplug; 270 | uint8_t APPLICATION_TYPE; 271 | uint8_t SECURITY_TYPE; 272 | uint8_t RunID [SLAC_RUNID_LEN]; 273 | uint8_t CipherSuiteSetSize; 274 | uint16_t CipherSuite [1]; 275 | }; 276 | 277 | struct __attribute__((packed)) cm_slac_param_confirm 278 | 279 | { 280 | struct ethernet_hdr ethernet; 281 | struct homeplug_fmi homeplug; 282 | uint8_t MSOUND_TARGET [ETHER_ADDR_LEN]; 283 | uint8_t NUM_SOUNDS; 284 | uint8_t TIME_OUT; 285 | uint8_t RESP_TYPE; 286 | uint8_t FORWARDING_STA [ETHER_ADDR_LEN]; 287 | uint8_t APPLICATION_TYPE; 288 | uint8_t SECURITY_TYPE; 289 | uint8_t RunID [SLAC_RUNID_LEN]; 290 | uint16_t CipherSuite; 291 | }; 292 | 293 | struct __attribute__((packed)) cm_start_atten_char_indicate 294 | 295 | { 296 | struct ethernet_hdr ethernet; 297 | struct homeplug_fmi homeplug; 298 | uint8_t APPLICATION_TYPE; 299 | uint8_t SECURITY_TYPE; 300 | struct __attribute__((packed)) 301 | { 302 | uint8_t NUM_SOUNDS; 303 | uint8_t TIME_OUT; 304 | uint8_t RESP_TYPE; 305 | uint8_t FORWARDING_STA [ETHER_ADDR_LEN]; 306 | uint8_t RunID [SLAC_RUNID_LEN]; 307 | } 308 | ACVarField; 309 | }; 310 | 311 | struct __attribute__((packed)) cm_start_atten_char_response 312 | 313 | { 314 | struct ethernet_hdr ethernet; 315 | struct homeplug_fmi homeplug; 316 | }; 317 | 318 | struct __attribute__((packed)) cm_atten_char_indicate 319 | 320 | { 321 | struct ethernet_hdr ethernet; 322 | struct homeplug_fmi homeplug; 323 | uint8_t APPLICATION_TYPE; 324 | uint8_t SECURITY_TYPE; 325 | struct __attribute__((packed)) 326 | { 327 | uint8_t SOURCE_ADDRESS [ETHER_ADDR_LEN]; 328 | uint8_t RunID [SLAC_RUNID_LEN]; 329 | uint8_t SOURCE_ID [SLAC_UNIQUE_ID_LEN]; 330 | uint8_t RESP_ID [SLAC_UNIQUE_ID_LEN]; 331 | uint8_t NUM_SOUNDS; 332 | struct __attribute__((packed)) 333 | { 334 | uint8_t NumGroups; 335 | uint8_t AAG [255]; 336 | } 337 | ATTEN_PROFILE; 338 | } 339 | ACVarField; 340 | }; 341 | 342 | struct __attribute__((packed)) cm_atten_char_response 343 | 344 | { 345 | struct ethernet_hdr ethernet; 346 | struct homeplug_fmi homeplug; 347 | uint8_t APPLICATION_TYPE; 348 | uint8_t SECURITY_TYPE; 349 | struct __attribute__((packed)) 350 | { 351 | uint8_t SOURCE_ADDRESS [ETHER_ADDR_LEN]; 352 | uint8_t RunID [SLAC_RUNID_LEN]; 353 | uint8_t SOURCE_ID [SLAC_UNIQUE_ID_LEN]; 354 | uint8_t RESP_ID [SLAC_UNIQUE_ID_LEN]; 355 | uint8_t Result; 356 | } 357 | ACVarField; 358 | }; 359 | 360 | struct __attribute__((packed)) cm_mnbc_sound_indicate 361 | 362 | { 363 | struct ethernet_hdr ethernet; 364 | struct homeplug_fmi homeplug; 365 | uint8_t APPLICATION_TYPE; 366 | uint8_t SECURITY_TYPE; 367 | struct __attribute__((packed)) 368 | { 369 | uint8_t SenderID [SLAC_UNIQUE_ID_LEN]; 370 | uint8_t CNT; 371 | uint8_t RunID [SLAC_RUNID_LEN]; 372 | uint8_t RND [SLAC_UNIQUE_ID_LEN]; 373 | } 374 | MSVarField; 375 | }; 376 | 377 | struct __attribute__((packed)) cm_validate_request 378 | 379 | { 380 | struct ethernet_hdr ethernet; 381 | struct homeplug_fmi homeplug; 382 | uint8_t SignalType; 383 | struct __attribute__((packed)) 384 | { 385 | uint8_t Timer; 386 | uint8_t Result; 387 | } 388 | VRVarField; 389 | }; 390 | 391 | struct __attribute__((packed)) cm_validate_confirm 392 | 393 | { 394 | struct ethernet_hdr ethernet; 395 | struct homeplug_fmi homeplug; 396 | uint8_t SignalType; 397 | struct __attribute__((packed)) 398 | { 399 | uint8_t ToggleNum; 400 | uint8_t Result; 401 | } 402 | VCVarField; 403 | }; 404 | 405 | struct __attribute__((packed)) cm_slac_match_request 406 | 407 | { 408 | struct ethernet_hdr ethernet; 409 | struct homeplug_fmi homeplug; 410 | uint8_t APPLICATION_TYPE; 411 | uint8_t SECURITY_TYPE; 412 | uint16_t MVFLength; 413 | struct __attribute__((packed)) 414 | { 415 | uint8_t PEV_ID [SLAC_UNIQUE_ID_LEN]; 416 | uint8_t PEV_MAC [ETHER_ADDR_LEN]; 417 | uint8_t EVSE_ID [SLAC_UNIQUE_ID_LEN]; 418 | uint8_t EVSE_MAC [ETHER_ADDR_LEN]; 419 | uint8_t RunID [SLAC_RUNID_LEN]; 420 | uint8_t RSVD [8]; 421 | } 422 | MatchVarField; 423 | }; 424 | 425 | struct __attribute__((packed)) cm_slac_match_confirm 426 | 427 | { 428 | struct ethernet_hdr ethernet; 429 | struct homeplug_fmi homeplug; 430 | uint8_t APPLICATION_TYPE; 431 | uint8_t SECURITY_TYPE; 432 | uint16_t MVFLength; 433 | struct __attribute__((packed)) 434 | { 435 | uint8_t PEV_ID [SLAC_UNIQUE_ID_LEN]; 436 | uint8_t PEV_MAC [ETHER_ADDR_LEN]; 437 | uint8_t EVSE_ID [SLAC_UNIQUE_ID_LEN]; 438 | uint8_t EVSE_MAC [ETHER_ADDR_LEN]; 439 | uint8_t RunID [SLAC_RUNID_LEN]; 440 | uint8_t RSVD1 [8]; 441 | uint8_t NID [SLAC_NID_LEN]; 442 | uint8_t RSVD2; 443 | uint8_t NMK [SLAC_NMK_LEN]; 444 | } 445 | MatchVarField; 446 | }; 447 | 448 | struct __attribute__((packed)) cm_atten_profile_indicate 449 | 450 | { 451 | struct ethernet_hdr ethernet; 452 | struct homeplug_fmi homeplug; 453 | uint8_t PEV_MAC [ETHER_ADDR_LEN]; 454 | uint8_t NumGroups; 455 | uint8_t RSVD; 456 | uint8_t AAG [255]; 457 | }; 458 | 459 | 460 | #ifndef __GNUC__ 461 | #pragma pack (pop) 462 | #endif 463 | 464 | /*====================================================================* 465 | * functions; 466 | *--------------------------------------------------------------------*/ 467 | /* 468 | signed pev_cm_slac_param (struct session *, struct channel *, struct message *); 469 | signed pev_cm_start_atten_char (struct session *, struct channel *, struct message *); 470 | signed pev_cm_atten_char (struct session *, struct channel *, struct message *); 471 | signed pev_cm_mnbc_sound (struct session *, struct channel *, struct message *); 472 | signed pev_cm_slac_match (struct session *, struct channel *, struct message *); 473 | signed pev_cm_set_key (struct session *, struct channel *, struct message *); 474 | signed evse_cm_slac_param (struct session *, struct channel *, struct message *); 475 | signed evse_cm_start_atten_char (struct session *, struct channel *, struct message *); 476 | signed evse_cm_atten_char (struct session *, struct channel *, struct message *); 477 | signed evse_cm_mnbc_sound (struct session *, struct channel *, struct message *); 478 | signed evse_cm_slac_match (struct session *, struct channel *, struct message *); 479 | signed evse_cm_set_key (struct session *, struct channel *, struct message *); 480 | signed slac_connect (struct session *); 481 | void slac_session (struct session *); 482 | void slac_structs (); 483 | signed slac_debug (struct session * session, signed status, char const * string, char const * format, ...); 484 | */ 485 | /*====================================================================* 486 | * 487 | *--------------------------------------------------------------------*/ 488 | 489 | #endif 490 | -------------------------------------------------------------------------------- /example/slac/slacassoc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "homeplug.h" 7 | #include "qualcomm_slac.h" 8 | #include "plc_eth.h" 9 | #include "../timeprofiling.h" 10 | 11 | int chattyslac = 0; 12 | typedef uint8_t byte; 13 | /*#define HOMEPLUG_MMV 0x01 14 | #define HOMEPLUG_MMTYPE 0x0000*/ 15 | #define HOMEPLUG_HDR_SIZE 5 16 | 17 | #define TIME_MICROSECOND 1000ULL 18 | #define TIME_MILLISECOND (TIME_MICROSECOND * 1000) 19 | #define TIME_SECOND (TIME_MILLISECOND * 1000) 20 | #define MSOUND_PAUSE (40 * TIME_MILLISECOND) 21 | #define SLAC_ATTENUATION_THRESHOLD 32 // DB 22 | #define SLAC_T_ASSOC_MNBC_TIMEOUT (1 * TIME_SECOND) 23 | #define SLAC_T_ASSOC_RESPONSE (200 * TIME_MILLISECOND) 24 | typedef unsigned long long uvlong; 25 | static inline uvlong nsleep(uvlong ns) 26 | { 27 | struct timespec left, ts = {.tv_sec = ns / TIME_SECOND, .tv_nsec = ns % TIME_SECOND}; 28 | int r = nanosleep(&ts, &left); 29 | assert(r == 0 || (r < 0 && errno == EINTR)); 30 | return (r == 0) ? 0 : ((uvlong)left.tv_sec * TIME_SECOND + (uvlong)ts.tv_nsec); 31 | } 32 | 33 | void ethhomeplughdr (void *vbuf, uint8_t mmv, uint16_t mmtype) 34 | { 35 | byte *buf = vbuf; 36 | buf[0] = mmv; // MMV 37 | buf[1] = mmtype & 0xFF; // written in little endian 38 | buf[2] = mmtype >> 8; 39 | buf[3] = 0; // FMSN 40 | buf[4] = 0; // FMID 41 | } 42 | 43 | int slac_verify_response(void *buf, uint8_t mmv, uint16_t mmtype) 44 | { 45 | struct homeplug *homeplug = (struct homeplug *)(buf); 46 | if (ntohs (homeplug->ethernet.MTYPE) != ETH_P_HPAV) { 47 | if (chattyslac) fprintf(stderr, "wrong eth type\n"); 48 | return -1; 49 | } 50 | if (homeplug->homeplug.MMV != mmv) { 51 | if (chattyslac) fprintf(stderr, "wrong mmv type %u\n", mmv); 52 | return -1; 53 | } 54 | if (le16toh(homeplug->homeplug.MMTYPE) != mmtype) { 55 | if (chattyslac) fprintf(stderr, "wrong mmtype 0x%x, expected 0x%x\n", homeplug->homeplug.MMTYPE, mmtype); 56 | return -1; 57 | } 58 | return 0; 59 | } 60 | 61 | static uint8_t GetValidValue(uint8_t val, uint8_t min, uint8_t max) { 62 | if (val < min) { 63 | return min; 64 | } 65 | if (val > max) { 66 | return max; 67 | } 68 | return val; 69 | } 70 | 71 | typedef struct iosendloop_args iosendloop_args_t; 72 | struct iosendloop_args{ 73 | ethconn_t *ethconn; 74 | byte *ethframe; 75 | size_t framelen; 76 | uvlong senddelay_ns; 77 | int max_tries; 78 | }; 79 | 80 | // Cancellable send loop, initiated which must be initiated with an IO-channel 81 | static ssize_t slac_iosendloop(void *vargs, atomic_int *cancel) 82 | { 83 | int err; 84 | bool infloop = true; 85 | int i; 86 | iosendloop_args_t *args = (iosendloop_args_t*)vargs; 87 | if (args->max_tries > 0) { 88 | i = 0; 89 | infloop = false; 90 | } 91 | while (atomic_load(cancel) == 0) { 92 | err = ethsend(args->ethconn, args->ethframe, args->framelen); 93 | if (err != 0) { 94 | return -1; 95 | } 96 | if (!infloop && i++ >= args->max_tries) { 97 | break; 98 | } 99 | nsleep(args->senddelay_ns); 100 | } 101 | return 0; 102 | } 103 | struct slac_iorecvloop_args{ 104 | ethconn_t *ethconn; 105 | byte *ethframe; 106 | byte mmv; 107 | uint16_t mmtype; 108 | }; 109 | // Cancellable receive/read loop, initiated which must be initiated with an IO-channel 110 | static ssize_t slac_iorecvloop(void *vargs, atomic_int *cancel) 111 | { 112 | int err; 113 | ssize_t n = -1; 114 | struct slac_iorecvloop_args *args = (struct slac_iorecvloop_args*)vargs; 115 | while (atomic_load(cancel) == 0) { 116 | n = ethrecv(args->ethconn, args->ethframe); 117 | if (n <= 0) { 118 | return -1; 119 | } 120 | err = slac_verify_response(args->ethframe, args->mmv, args->mmtype); 121 | if (err != 0) { 122 | continue; 123 | } 124 | break; 125 | } 126 | return n; 127 | } 128 | 129 | // Cancellable ethnernet receive/read (cancelled with a send on channel chnc) 130 | ssize_t slac_recv_c(ethconn_t *ethconn, void *ethframe, byte mmv, uint16_t mmtype, Chan *chnc) 131 | { 132 | ssize_t ret; 133 | ssize_t err; 134 | Chan *iocr = iochan(1048576 - PTHREAD_STACK_MIN); 135 | Alt alts[] = {{ .c = iocr, .v = &ret, .op = CHANRECV }, 136 | { .c = chnc, .v = NULL, .op = CHANRECV }, 137 | { .op = CHANEND }}; 138 | struct slac_iorecvloop_args rargs = {.ethconn = ethconn, 139 | .ethframe = ethframe, 140 | .mmv = mmv, 141 | .mmtype = mmtype}; 142 | if (iocr == NULL) { 143 | if (chattyslac) fprintf(stderr, "slac_recv_c: iochan err\n"); 144 | return -1; 145 | } 146 | iocall(iocr, &slac_iorecvloop, &rargs, sizeof(rargs)); 147 | switch (alt(alts)) { 148 | case 0: // Done reading response 149 | if (chattyslac) fprintf(stderr, "received valid response\n"); 150 | err = ret; 151 | break; 152 | case 1: // Done writing and no response -> error 153 | iocancel(iocr); 154 | err = -1; 155 | if (chattyslac) fprintf(stderr, "timeout\n"); 156 | break; 157 | default: 158 | if (chattyslac) fprintf(stderr, "critical ev_sdp_discover_evse: alt error\n"); 159 | abort(); 160 | } 161 | chanfree(iocr); 162 | return err; 163 | } 164 | 165 | // Sends an ethframe over ethconn every send_delay nanoseconds up to 166 | // max_send_tries times until a message with the mmv recv_mmv and the mmtype 167 | // recv_mmtype is received. 168 | // !! Note that while the framelen can vary, the underlying 169 | // ethframe buffer must always be at least ETH_FRAME_LEN bytes. !! 170 | ssize_t slac_sendrecvloop(ethconn_t *ethconn, void *ethframe, size_t framelen, 171 | byte recv_mmv, uint16_t recv_mmtype, 172 | int max_send_tries, uvlong senddelay_ns) 173 | { 174 | ssize_t ret; 175 | ssize_t err; 176 | Chan *iocr = iochan(1048576 - PTHREAD_STACK_MIN); 177 | Chan *iocs = iochan(1048576 - PTHREAD_STACK_MIN); 178 | Alt alts[] = {{ .c = iocr, .v = &ret, .op = CHANRECV }, 179 | { .c = iocs, .v = &ret, .op = CHANRECV }, 180 | { .op = CHANEND }}; 181 | struct slac_iorecvloop_args rargs = {.ethconn = ethconn, 182 | .ethframe = ethframe, 183 | .mmv = recv_mmv, 184 | .mmtype = recv_mmtype}; 185 | iosendloop_args_t sargs = {.ethconn = ethconn, 186 | .ethframe = ethframe, 187 | .framelen = framelen, 188 | .senddelay_ns = senddelay_ns, 189 | .max_tries = max_send_tries}; 190 | if (iocr == NULL || iocs == NULL) { 191 | if (chattyslac) fprintf(stderr, "slac_sendrecvloop: iochan error\n"); 192 | if (iocr != NULL) { 193 | chanfree(iocr); 194 | } 195 | if (iocs != NULL) { 196 | chanfree(iocs); 197 | } 198 | return -1; 199 | } 200 | iocall(iocr, &slac_iorecvloop, &rargs, sizeof(rargs)); 201 | iocall(iocs, &slac_iosendloop, &sargs, sizeof(sargs)); 202 | switch (alt(alts)) { 203 | case 0: // Done reading response 204 | iocancel(iocs); 205 | if (chattyslac) fprintf(stderr, "received valid response\n"); 206 | err = ret; 207 | break; 208 | case 1: // Done writing and no response -> error 209 | iocancel(iocr); 210 | err = -1; 211 | break; 212 | default: 213 | if (chattyslac) fprintf(stderr, "critical ev_sdp_discover_evse: alt error\n"); 214 | abort(); 215 | } 216 | chanfree(iocr); 217 | chanfree(iocs); 218 | return err; 219 | } 220 | 221 | // === Slac parameter discovery === 222 | int slac_cm_param_req(ethconn_t *ethconn, struct slac_session *session) 223 | { 224 | byte ethframe[ETH_FRAME_LEN]; 225 | ssize_t n; 226 | struct cm_slac_param_request *req = (struct cm_slac_param_request*)ethframe; 227 | struct cm_slac_param_confirm *confirm = (struct cm_slac_param_confirm *) (ethframe); 228 | ethwritehdr(&req->ethernet, ethconn, ETH_BROADCAST_ADDR); 229 | ethhomeplughdr(&req->homeplug, HOMEPLUG_MMV, (CM_SLAC_PARAM | MMTYPE_REQ)); 230 | req->APPLICATION_TYPE = SLAC_APPLICATION_TYPE; 231 | req->SECURITY_TYPE = SLAC_SECURITY_TYPE; 232 | memcpy(req->RunID, session->RunID, sizeof(session->RunID)); 233 | req->CipherSuiteSetSize = 0; 234 | req->CipherSuite [0] = htole16(0); 235 | /*request->CipherSuite [0] = HTOLE16 ((uint16_t) (session->counter));*/ 236 | 237 | n = slac_sendrecvloop(ethconn, ethframe, sizeof(*req), 238 | HOMEPLUG_MMV, (CM_SLAC_PARAM | MMTYPE_CNF), 239 | 0, 250 *TIME_MILLISECOND); 240 | if (n <= 0) { 241 | if (chattyslac) fprintf(stderr, "slac_cm_param_req error\n"); 242 | return -1; 243 | } 244 | if (confirm->APPLICATION_TYPE != SLAC_APPLICATION_TYPE) { 245 | if (chattyslac) fprintf(stderr, "slac_cm_param_req: invalid slac application type\n"); 246 | return -1; 247 | } 248 | if (confirm->SECURITY_TYPE != SLAC_SECURITY_TYPE) { 249 | if (chattyslac) fprintf(stderr, "slac_cm_param_req: invalid slac security type\n"); 250 | return -1; 251 | } 252 | //memcpy(session->EVSE_MAC, confirm->ethernet.OSA, ETH_ALEN); 253 | memcpy(session->FORWARDING_STA, confirm->FORWARDING_STA, sizeof (session->FORWARDING_STA)); 254 | session->NUM_SOUNDS = GetValidValue(confirm->NUM_SOUNDS, 8, 16); 255 | session->TIME_OUT = confirm->TIME_OUT; 256 | session->RESP_TYPE = confirm->RESP_TYPE; 257 | return 0; 258 | } 259 | 260 | // === Start Attenuation Characterization === 261 | int slac_cm_start_atten_char(ethconn_t *ethconn, struct slac_session *session) 262 | { 263 | int err, i; 264 | byte ethframe[ETH_FRAME_LEN]; 265 | struct cm_start_atten_char_indicate *indicate = (struct cm_start_atten_char_indicate *) (ethframe); 266 | ethwritehdr(&indicate->ethernet, ethconn, ETH_BROADCAST_ADDR); 267 | ethhomeplughdr(&indicate->homeplug, HOMEPLUG_MMV, (CM_START_ATTEN_CHAR | MMTYPE_IND)); 268 | indicate->APPLICATION_TYPE = SLAC_APPLICATION_TYPE; 269 | indicate->SECURITY_TYPE = SLAC_SECURITY_TYPE; 270 | indicate->ACVarField.NUM_SOUNDS = session->NUM_SOUNDS; 271 | indicate->ACVarField.TIME_OUT = session->TIME_OUT; 272 | indicate->ACVarField.RESP_TYPE = session->RESP_TYPE; 273 | memcpy (indicate->ACVarField.FORWARDING_STA, session->FORWARDING_STA, sizeof (indicate->ACVarField.FORWARDING_STA)); 274 | memcpy (indicate->ACVarField.RunID, session->RunID, sizeof (indicate->ACVarField.RunID)); 275 | for (i = 0; i < 3; i++) { 276 | err = ethsend(ethconn, ethframe, sizeof(*indicate)); 277 | if (err != 0) { 278 | if (chattyslac) fprintf(stderr, "slac_cm_start_atten_char: ethsend err\n"); 279 | return -1; 280 | } 281 | } 282 | return 0; 283 | } 284 | 285 | int slac_cm_mnbc_sound(ethconn_t *ethconn, struct slac_session * session) 286 | { 287 | int err; 288 | byte ethframe[ETH_FRAME_LEN]; 289 | int sound = session->NUM_SOUNDS; 290 | struct cm_mnbc_sound_indicate *indicate = (struct cm_mnbc_sound_indicate *) (ethframe); 291 | while (sound--) { 292 | ethwritehdr(&indicate->ethernet, ethconn, ETH_BROADCAST_ADDR); 293 | ethhomeplughdr(&indicate->homeplug, HOMEPLUG_MMV, (CM_MNBC_SOUND | MMTYPE_IND)); 294 | indicate->APPLICATION_TYPE = SLAC_APPLICATION_TYPE; 295 | indicate->SECURITY_TYPE = SLAC_SECURITY_TYPE; 296 | memcpy(indicate->MSVarField.SenderID, session->PEV_ID, sizeof (indicate->MSVarField.SenderID)); 297 | indicate->MSVarField.CNT = sound; 298 | memcpy(indicate->MSVarField.RunID, session->RunID, sizeof (indicate->MSVarField.RunID)); 299 | memset (indicate->MSVarField.RND, 0, sizeof (indicate->MSVarField.RND)); 300 | err = ethsend(ethconn, ethframe, sizeof(*indicate)); 301 | //err = 1; 302 | if (err != 0) { 303 | if (chattyslac) fprintf(stderr, "ethsend err\n"); 304 | return -1; 305 | } 306 | nsleep(MSOUND_PAUSE); 307 | } 308 | return 0; 309 | } 310 | 311 | int slac_check_attn(byte AAG[SLAC_GROUPS], byte numgroups, unsigned limit) 312 | { 313 | unsigned int avg, i; 314 | unsigned int total = 0; 315 | if (numgroups == 0) { 316 | if (chattyslac) fprintf(stderr, "slac_check_attn: no atten groups\n"); 317 | return -1; 318 | } 319 | for (i = 0; i < numgroups; i++) { 320 | total += AAG[i]; 321 | } 322 | avg = total / i; 323 | if (avg > limit) { 324 | if (chattyslac) fprintf(stderr, "slac_check_attn: Average attenuation %udB high\n", avg); 325 | return -1; 326 | } 327 | if (chattyslac) fprintf(stderr, "Success: Average attenuation %udB, is less than %udB\n", avg, SLAC_ATTENUATION_THRESHOLD); 328 | return 0; 329 | } 330 | 331 | // Receive attenchar request from EVSE and respond 332 | int slac_cm_atten_char(ethconn_t *ethconn, struct slac_session *session) 333 | { 334 | int err; 335 | ssize_t n; 336 | Chan tc; 337 | byte ethframe[ETH_FRAME_LEN]; 338 | struct cm_atten_char_indicate * indicate = (struct cm_atten_char_indicate *) (ethframe); 339 | struct cm_atten_char_response * response = (struct cm_atten_char_response *) (ethframe); 340 | err = tchaninit(&tc); 341 | if (err != 0) { 342 | return -1; 343 | } 344 | tchanset(&tc, SLAC_T_ASSOC_MNBC_TIMEOUT); 345 | for (;;) { 346 | n = slac_recv_c(ethconn, ethframe, HOMEPLUG_MMV, (CM_ATTEN_CHAR | MMTYPE_IND), &tc); 347 | if (n <= 0) { 348 | if (chattyslac) fprintf(stderr, "slac_cm_atten_char: slac_recv_c err\n"); 349 | chanfree(&tc); 350 | return -1; 351 | } 352 | err = slac_check_attn(indicate->ACVarField.ATTEN_PROFILE.AAG, indicate->ACVarField.ATTEN_PROFILE.NumGroups,session->limit); 353 | if (err != 0) { 354 | if (chattyslac) fprintf(stderr, "slac_associate: slac_check_attn error\n"); 355 | continue; 356 | } 357 | break; 358 | } 359 | memcpy (session->EVSE_MAC, indicate->ethernet.OSA, ETH_ALEN); 360 | session->NUM_SOUNDS = indicate->ACVarField.NUM_SOUNDS; 361 | // === Start writing response === 362 | ethwritehdr(&response->ethernet, ethconn, session->EVSE_MAC); 363 | ethhomeplughdr(&response->homeplug, HOMEPLUG_MMV, (CM_ATTEN_CHAR | MMTYPE_RSP)); 364 | response->APPLICATION_TYPE = SLAC_APPLICATION_TYPE; 365 | response->SECURITY_TYPE = SLAC_SECURITY_TYPE; 366 | memcpy (response->ACVarField.SOURCE_ADDRESS, ethconn->src_mac, ETH_ALEN); 367 | memcpy (response->ACVarField.RunID, session->RunID, 368 | sizeof (response->ACVarField.RunID)); 369 | memset (response->ACVarField.SOURCE_ID, 0, 370 | sizeof (response->ACVarField.SOURCE_ID)); 371 | memset (response->ACVarField.RESP_ID, 0, 372 | sizeof (response->ACVarField.RESP_ID)); 373 | response->ACVarField.Result = 0; 374 | err = ethsend(ethconn, ethframe, sizeof(*response)); 375 | if (err != 0) { 376 | if (chattyslac) fprintf(stderr, "slac_cm_atten_char: ethsend err\n"); 377 | chanfree(&tc); 378 | return -1; 379 | } 380 | chanfree(&tc); 381 | return 0; 382 | } 383 | 384 | int slac_cm_match_request(ethconn_t *ethconn, struct slac_session *session) 385 | { 386 | int err; 387 | ssize_t n; 388 | byte ethframe[ETH_FRAME_LEN]; 389 | Chan tc; 390 | struct cm_slac_match_request * request = (struct cm_slac_match_request *) (ethframe); 391 | struct cm_slac_match_confirm * confirm = (struct cm_slac_match_confirm *) (ethframe); 392 | ethwritehdr(&request->ethernet, ethconn, session->EVSE_MAC); 393 | ethhomeplughdr(&request->homeplug, HOMEPLUG_MMV, (CM_SLAC_MATCH | MMTYPE_REQ)); 394 | request->APPLICATION_TYPE = SLAC_APPLICATION_TYPE; 395 | request->SECURITY_TYPE = SLAC_SECURITY_TYPE; 396 | request->MVFLength = htole16 (sizeof (request->MatchVarField)); 397 | memcpy (request->MatchVarField.PEV_ID, session->PEV_ID, 398 | sizeof (request->MatchVarField.PEV_ID)); 399 | memcpy(request->MatchVarField.PEV_MAC, ethconn->src_mac, ETH_ALEN); 400 | memcpy(request->MatchVarField.RunID, session->RunID, 401 | sizeof (request->MatchVarField.RunID)); 402 | memset(request->MatchVarField.RSVD, 0, sizeof(request->MatchVarField.RSVD)); 403 | err = ethsend(ethconn, ethframe, sizeof(*request)); 404 | if (err != 0) { 405 | if (chattyslac) fprintf(stderr, "slac_cm_match_request: ethsend error\n"); 406 | return -1; 407 | } 408 | err = tchaninit(&tc); 409 | if (err != 0) { 410 | if (chattyslac) fprintf(stderr, "tchaninit err\n"); 411 | return -1; 412 | } 413 | tchanset(&tc, SLAC_T_ASSOC_RESPONSE); 414 | for(;;) { 415 | n = slac_recv_c(ethconn, ethframe, HOMEPLUG_MMV, (CM_SLAC_MATCH | MMTYPE_CNF), &tc); 416 | if (n <= 0) { 417 | if (chattyslac) fprintf(stderr, "slac_cm_match_request: slac_recv err\n"); 418 | chanfree(&tc); 419 | return -1; 420 | } 421 | if (memcmp (session->RunID, confirm->MatchVarField.RunID, 422 | sizeof (session->RunID)) != 0) { 423 | if (chattyslac) fprintf(stderr, "slac_cm_match_request: invalid runid error, repeating recv\n"); 424 | continue; 425 | } 426 | break; 427 | } 428 | memcpy (session->EVSE_ID, confirm->MatchVarField.EVSE_ID, sizeof (session->EVSE_ID)); 429 | memcpy (session->EVSE_MAC, confirm->MatchVarField.EVSE_MAC, ETH_ALEN); 430 | memcpy (session->NMK, confirm->MatchVarField.NMK, sizeof (session->NMK)); 431 | memcpy (session->NID, confirm->MatchVarField.NID, sizeof (session->NID)); 432 | chanfree(&tc); 433 | return 0; 434 | } 435 | 436 | int slac_cm_set_key(ethconn_t *ethconn, struct slac_session *session) 437 | { 438 | int err; 439 | ssize_t n; 440 | byte ethframe[ETH_FRAME_LEN]; 441 | Chan tc; 442 | struct cm_set_key_request * request = (struct cm_set_key_request *) (ethframe); 443 | struct cm_set_key_confirm * confirm = (struct cm_set_key_confirm *) (ethframe); 444 | 445 | // === Configure "Set Network Key" request === 446 | ethwritehdr(&request->ethernet, ethconn, ETH_LOCAL_ATHEROS_DEVICE); 447 | ethhomeplughdr(&request->homeplug, HOMEPLUG_MMV, (CM_SET_KEY | MMTYPE_REQ)); 448 | request->KEYTYPE = SLAC_CM_SETKEY_KEYTYPE; 449 | memset(& request->MYNOUNCE, 0xAA, sizeof (request->MYNOUNCE)); 450 | memset(& request->YOURNOUNCE, 0x00, sizeof (request->YOURNOUNCE)); 451 | request->PID = SLAC_CM_SETKEY_PID; 452 | request->PRN = htole16 (SLAC_CM_SETKEY_PRN); 453 | request->PMN = SLAC_CM_SETKEY_PMN; 454 | request->CCOCAP = SLAC_CM_SETKEY_CCO; 455 | memcpy(request->NID, session->NID, sizeof (request->NID)); 456 | request->NEWEKS = SLAC_CM_SETKEY_EKS; 457 | memcpy(request->NEWKEY, session->NMK, sizeof (request->NEWKEY)); 458 | memset(request->RSVD, 0, sizeof(request->RSVD)); 459 | err = ethsend(ethconn, ethframe, sizeof(*request)); 460 | if (err != 0) { 461 | if (chattyslac) fprintf(stderr, "slac_cm_set_key_request: ethsend error\n"); 462 | return -1; 463 | } 464 | err = tchaninit(&tc); 465 | if (err != 0) { 466 | if (chattyslac) fprintf(stderr, "tchaninit err\n"); 467 | return -1; 468 | } 469 | tchanset(&tc, SLAC_T_ASSOC_RESPONSE); 470 | if (chattyslac) fprintf(stderr, "====####========== SET KEY ======####======\n"); 471 | n = slac_recv_c(ethconn, ethframe, HOMEPLUG_MMV, (CM_SET_KEY | MMTYPE_CNF), &tc); 472 | if (n <= 0) { 473 | if (chattyslac) fprintf(stderr, "slac_cm_set_key_request: slac_recv_c err\n"); 474 | chanfree(&tc); 475 | return -1; 476 | } 477 | if (confirm->RESULT == 0) { 478 | if (chattyslac) fprintf(stderr, "slac_cm_set_key_request: error result\n"); 479 | chanfree(&tc); 480 | return -1; 481 | } 482 | chanfree(&tc); 483 | return 0; 484 | } 485 | 486 | int slac_associate(const char *if_name) 487 | { 488 | struct slac_session ses; 489 | ethconn_t ethconn; 490 | byte pev_id[17] = {0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0x00, 491 | 0x11,0x22,0x33,0x44,0x55,0x66,0x77}; 492 | int err; 493 | tlog tl; 494 | static uint8_t runid_counter = 0; 495 | err = ethdial(ðconn, if_name, ETH_P_HPAV); 496 | if (err != 0) { 497 | if (chattyslac) fprintf(stderr, "ethdial err %m\n"); 498 | return -1; 499 | } 500 | memset(&ses, 0, sizeof(ses)); 501 | memcpy(ses.RunID, ethconn.src_mac, ETH_ALEN); 502 | ses.RunID[6] = 0x00; 503 | ses.RunID[7] = runid_counter++; 504 | memcpy(ses.PEV_ID, pev_id, 17); 505 | ses.limit = SLAC_ATTENUATION_THRESHOLD; 506 | 507 | tl_init(&tl, "ISO 15118 SLAC Communication Timings"); 508 | err = slac_cm_param_req(ðconn, &ses); 509 | if (err != 0) { 510 | if (chattyslac) fprintf(stderr, "slac_associate: slac_cm_param_req error\n"); 511 | return -1; 512 | } 513 | tl_register(&tl, "CM_PARM"); 514 | if (chattyslac) fprintf(stderr, "Start atten char\n"); 515 | err = slac_cm_start_atten_char(ðconn, &ses); 516 | if (err != 0) { 517 | if (chattyslac) fprintf(stderr, "slac_associate: slac_cm_mnbc_sound error\n"); 518 | return -1; 519 | } 520 | tl_register(&tl, "CM_START_ATTEN_CHAR"); 521 | if (chattyslac) fprintf(stderr, "Send sounds\n"); 522 | err = slac_cm_mnbc_sound(ðconn, &ses); 523 | if (err != 0) { 524 | if (chattyslac) fprintf(stderr, "slac_associate: slac_cm_mnbc_sound error\n"); 525 | return -1; 526 | } 527 | tl_register(&tl, "CM_MNBC_SOUND"); 528 | if (chattyslac) fprintf(stderr, "Receive atten char response\n"); 529 | err = slac_cm_atten_char(ðconn, &ses); 530 | if (err != 0) { 531 | if (chattyslac) fprintf(stderr, "slac_associate: slac_cm_atten_char error\n"); 532 | return -1; 533 | } 534 | tl_register(&tl, "CM_ATTEN_CHAR"); 535 | if (chattyslac) fprintf(stderr, "Slac match request\n"); 536 | err = slac_cm_match_request(ðconn, &ses); 537 | if (err != 0) { 538 | if (chattyslac) fprintf(stderr, "slac_associate: slac_cm_match_request error\n"); 539 | return -1; 540 | } 541 | tl_register(&tl, "CM_SLAC_MATCH"); 542 | if (chattyslac) fprintf(stderr, "set key\n"); 543 | err = slac_cm_set_key(ðconn, &ses); 544 | if (err != 0) { 545 | if (chattyslac) fprintf(stderr, "slac_associate: slac_cm_set_key error\n"); 546 | return -1; 547 | } 548 | tl_register(&tl, "CM_SET_KEY"); 549 | tl_print(&tl); 550 | if (chattyslac) fprintf(stderr, "done\n"); 551 | return 0; 552 | } 553 | -------------------------------------------------------------------------------- /example/slac/slacassoc.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef SLACASSOC_H 3 | #define SLACASSOC 1 4 | extern int chattyslac; 5 | int slac_associate(const char *if_name); 6 | #endif 7 | -------------------------------------------------------------------------------- /example/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "slac/plc_eth.h" 11 | #include "slac/slacassoc.h" 12 | #include "client.h" 13 | #include "server.h" 14 | #include "timeprofiling.h" 15 | 16 | void plgp_slac_listen(const char *if_name, const uint8_t dest_mac_evse[6]); 17 | 18 | static const uint8_t EVMAC[6] = {0x00, 0x05, 0xB6, 0x01, 0x86, 0xBD}; 19 | static const uint8_t EVSEMAC[6] = {0x00, 0x05, 0xB6, 0x01, 0x88, 0xA3}; 20 | 21 | static const char *argv0; 22 | 23 | static void fatal(const char *fmt, ...) 24 | { 25 | va_list ap; 26 | 27 | fprintf(stderr, "%s: ", argv0); 28 | va_start(ap, fmt); 29 | vfprintf(stderr, fmt, ap); 30 | va_end(ap); 31 | fprintf(stderr, "\n"); 32 | 33 | exit(1); 34 | } 35 | 36 | static void ev(const char *if_name, bool tls_enabled) 37 | { 38 | evcc_conn_t conn; 39 | ev_session_t s; 40 | tlog tl; 41 | memset(&conn, 0, sizeof(evcc_conn_t)); 42 | memset(&s, 0, sizeof(s)); 43 | int err; 44 | int ncycles = 0; 45 | if (load_contract("certs/contractchain.pem", "certs/contract.key", &s) != 0) { 46 | fatal("can't load certs/contract.key: %m"); 47 | } 48 | tl_init(&tl, "ISO 15118 Post SLAC Communication Timings"); 49 | if (chattyv2g) printf("Starting sdp:\n"); 50 | if (ev_sdp_discover_evse(if_name, &conn.addr, tls_enabled) < 0) { 51 | fatal("failed to discover EVSE on interface %s", if_name); 52 | } 53 | tl_register(&tl, "SDP"); 54 | if (chattyv2g) printf("connecting to secc\n"); 55 | if (tls_enabled) { 56 | err = evcc_connect_tls(&conn, "certs/ev.pem", "certs/ev.key"); 57 | } else { 58 | err = evcc_connect_tcp(&conn); 59 | } 60 | if (err != 0) { 61 | printf("main: evcc_connect_tls error\n"); 62 | return; 63 | } 64 | tl_register(&tl, "Connection & apphandshake"); 65 | if (chattyv2g) printf("session setup request\n"); 66 | err = session_request(&conn, &s); 67 | if (err != 0) { 68 | printf("RIP session_request\n"); 69 | return; 70 | } 71 | tl_register(&tl, "Session req"); 72 | if (chattyv2g) printf("service discovery request\n"); 73 | err = service_discovery_request(&conn, &s); 74 | if (err != 0) { 75 | printf("ev_example: service discovery request err\n"); 76 | return; 77 | } 78 | tl_register(&tl, "Service disc. req"); 79 | if (chattyv2g) printf("payment selection request\n"); 80 | err = payment_selection_request(&conn, &s); 81 | if (err != 0) { 82 | printf("ev_example: payment_selection_request err\n"); 83 | return; 84 | } 85 | tl_register(&tl, "Payment select. req"); 86 | if (chattyv2g) printf("payment details request\n"); 87 | if (!s.charging_is_free) { 88 | err = payment_details_request(&conn, &s); 89 | if (err != 0) { 90 | printf("ev_example: payment_selection_request err\n"); 91 | return; 92 | } 93 | } 94 | tl_register(&tl, "Payment details req"); 95 | if (chattyv2g) printf("authorization request\n"); 96 | err = authorization_request(&conn, &s); 97 | if (err != 0) { 98 | printf("ev_example: authorization_request err\n"); 99 | return; 100 | } 101 | tl_register(&tl, "Auth. req"); 102 | if (chattyv2g) printf("charge parameter request\n"); 103 | charging_negotiation: 104 | err = charge_parameter_request(&conn, &s); 105 | if (err != 0) { 106 | printf("ev_example: charge_parameter_request err\n"); 107 | return; 108 | } 109 | tl_register(&tl, "Charge param. req"); 110 | if (chattyv2g) printf("power delivery request\n"); 111 | err = power_delivery_request(&conn, &s, v2gchargeProgressType_Start); 112 | if (err != 0) { 113 | printf("ev_example: power_delivery start request err\n"); 114 | return; 115 | } 116 | tl_register(&tl, "Power deliv. req"); 117 | if (chattyv2g) printf("Charging (repeating charging status requests)\n"); 118 | for (;ncycles < 5; ncycles++) { 119 | err = charging_status_request(&conn, &s); 120 | if (err != 0) { 121 | printf("ev_example: charging_status_request err\n"); 122 | return; 123 | } 124 | tl_register(&tl, "Charging status req"); 125 | if (s.evse_notification == v2gEVSENotificationType_StopCharging) { 126 | printf("ev_example: EVSE has prompted charging to stop\n"); 127 | break; 128 | } else if (s.evse_notification == v2gEVSENotificationType_ReNegotiation) { 129 | goto charging_negotiation; 130 | } 131 | printf("="); 132 | fflush(stdout); 133 | if (enable_timeprofiling) { 134 | break; 135 | } 136 | sleep(1); 137 | } 138 | if (chattyv2g) printf("Performing power delivery stop request\n"); 139 | err = power_delivery_request(&conn, &s, v2gchargeProgressType_Stop); 140 | if (err != 0) { 141 | printf("ev_example: power_delivery_request err\n"); 142 | return; 143 | } 144 | tl_register(&tl, "Power delivery stop req"); 145 | if (chattyv2g) printf("Performing session stop request\n"); 146 | err = session_stop_request(&conn, &s); 147 | if (err != 0) { 148 | printf("ev_example: session_stop_request err\n"); 149 | return; 150 | } 151 | tl_register(&tl, "Session stop req"); 152 | tl_print(&tl); 153 | evcc_close_conn(&conn); 154 | evcc_session_cleanup(&s); 155 | if (chattyv2g) printf("Succesfully finished charging, ending session\n"); 156 | } 157 | 158 | static void evse(const char *if_name) 159 | { 160 | int tls_port, tcp_port, tls_sockfd, tcp_sockfd; 161 | 162 | // Init the contract root certificates 163 | int err = x509_crt_parse_path(&Trusted_contract_rootcert_chain, 164 | "certs/root/mobilityop/certs/"); 165 | if (err != 0) { 166 | printf("evse_example: Unable to load contract root certificates\n"); 167 | char strerr[256]; 168 | polarssl_strerror(err, strerr, 256); 169 | printf("err = %s\n", strerr); 170 | return; 171 | } 172 | init_sessions(); 173 | // === Bind to dynamic port === 174 | tls_sockfd = bind_v2gport(&tls_port); 175 | if (tls_sockfd < 0) { 176 | printf("secc_bind_tls returned %d\n", tls_sockfd); 177 | return; 178 | } 179 | tcp_sockfd = bind_v2gport(&tcp_port); 180 | if (tcp_sockfd < 0) { 181 | printf("secc_bind_tls returned %d\n", tcp_sockfd); 182 | return; 183 | } 184 | printf("start sdp listen\n"); 185 | secc_listen_tls(tls_sockfd, &create_response_message, "certs/evse.pem", "certs/evse.key"); 186 | secc_listen_tcp(tcp_sockfd, &create_response_message); 187 | // Set port to 0 to disable tls or tcp 188 | // (always do sdp_listen after secc_listen_*) 189 | sdp_listen(if_name, tls_port, tcp_port); 190 | } 191 | 192 | void usage(void) 193 | { 194 | fprintf(stderr, "Usage: %s [-sv] [--] interface node-type\n", argv0); 195 | exit(1); 196 | } 197 | 198 | void 199 | threadmain(int argc, 200 | char *argv[]) 201 | { 202 | enum { EV, EVSE }; 203 | const char *iface, *type; 204 | int opt, slac = 0, notls = 0; 205 | 206 | argv0 = argv[0]; 207 | while ((opt = getopt(argc, argv, "svnft")) != -1) { 208 | switch (opt) { 209 | case 's': // Enable SLAC 210 | slac++; 211 | break; 212 | 213 | case 'v': // Verbose 214 | chattyv2g++; 215 | chattyslac++; 216 | break; 217 | case 'n': // no tls 218 | notls++; 219 | break; 220 | case 'f': 221 | secc_free_charge++; 222 | break; 223 | case 't': 224 | enable_timeprofiling++; 225 | break; 226 | default: 227 | usage(); 228 | } 229 | } 230 | if (optind + 1 >= argc) { usage(); } 231 | 232 | iface = argv[optind]; 233 | type = argv[optind + 1]; 234 | if (strcasecmp(type, "EVSE") == 0) { 235 | switch_power_line(iface, EVSEMAC, false); 236 | if (slac) { 237 | printf("SLAC enabled\n"); 238 | plgp_slac_listen(iface, EVSEMAC); 239 | } 240 | evse(iface); 241 | } else if (strcasecmp(type, "EV") == 0) { 242 | if (slac) { 243 | switch_power_line(iface, EVMAC, false); 244 | printf("=== STARTING SLAC ASSOCIATION ===\n"); 245 | while(slac_associate(iface) != 0) { 246 | printf("something went wrong, trying again\n"); 247 | } 248 | printf("Slac is done. Waiting 8 seconds for networks to form.\n"); 249 | sleep(8); 250 | } 251 | ev(iface, !notls); 252 | } else { 253 | fatal("node type must be EV or EVSE"); 254 | } 255 | printf("Exiting\n"); 256 | exit(0); 257 | } 258 | -------------------------------------------------------------------------------- /example/timeprofiling.c: -------------------------------------------------------------------------------- 1 | #include "timeprofiling.h" 2 | #include "string.h" 3 | #include 4 | 5 | int enable_timeprofiling = 0; 6 | 7 | void tl_init(tlog *tl, const char* title) { 8 | clock_gettime(CLOCK_MONOTONIC, &tl->reference); 9 | strcpy(tl->title, title); 10 | tl->n = 0; 11 | } 12 | 13 | void tl_register(tlog *tl, const char *label) 14 | { 15 | if (enable_timeprofiling && tl->n < 128) { 16 | tentry *tent = &tl->entry[tl->n]; 17 | strcpy(tent->label, label); 18 | clock_gettime(CLOCK_MONOTONIC, &tent->t); 19 | tl->n++; 20 | } 21 | } 22 | 23 | void tl_print(tlog* tl) 24 | { 25 | int i; 26 | struct timespec *ref = &tl->reference; 27 | struct timespec temp; 28 | if (enable_timeprofiling) { 29 | printf("=== %s ===\n", tl->title); 30 | for (i = 0; i < tl->n; i++) { 31 | temp.tv_sec = tl->entry[i].t.tv_sec - ref->tv_sec; 32 | temp.tv_nsec = tl->entry[i].t.tv_nsec - ref->tv_nsec; 33 | printf("%s: %luus\n", tl->entry[i].label, temp.tv_sec * 1000000 + temp.tv_nsec /1000); 34 | } 35 | printf("=======\n"); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /example/timeprofiling.h: -------------------------------------------------------------------------------- 1 | #ifndef TIMEPROFILING_H 2 | #define TIMEPROFILING_H 3 | #include 4 | extern int enable_timeprofiling; 5 | 6 | typedef struct tentry tentry; 7 | struct tentry { 8 | char label[64]; 9 | struct timespec t; 10 | }; 11 | typedef struct tlog tlog; 12 | struct tlog { 13 | char title[128]; 14 | tentry entry[128]; 15 | int n; 16 | struct timespec reference; 17 | }; 18 | void tl_init(tlog *tl, const char* title); 19 | void tl_register(tlog *tl, const char *label); 20 | void tl_print(tlog* tl); 21 | #endif 22 | -------------------------------------------------------------------------------- /map.c: -------------------------------------------------------------------------------- 1 | #include "map.h" 2 | 3 | int 4 | mapinit(Map *map, 5 | size_t elemsz, 6 | size_t nbuckets, 7 | size_t (*hash)(Key), 8 | int (*cmp)(Key, Key)) 9 | { 10 | map->hash = hash; 11 | map->cmp = cmp; 12 | 13 | map->elemsz = elemsz; 14 | map->nbuckets = nbuckets; 15 | map->buckets = calloc(nbuckets, sizeof(void *)); 16 | return map->buckets ? 0 : -1; 17 | } 18 | 19 | void * 20 | mapinsert(Map *map, 21 | Key k) 22 | { 23 | size_t h = map->hash(k) % map->nbuckets; 24 | Bucket *b = malloc(sizeof(Bucket) + map->elemsz); 25 | if (!b) { return NULL; } 26 | 27 | b->k = k; 28 | b->next = map->buckets[h]; 29 | map->buckets[h] = b; 30 | 31 | return b->v; 32 | } 33 | 34 | void * 35 | mapfind(Map *map, 36 | Key k) 37 | { 38 | size_t h = map->hash(k) % map->nbuckets; 39 | Bucket *b; 40 | 41 | for (b = map->buckets[h]; b; b = b->next) { 42 | if (map->cmp(k, b->k) == 0) { return b->v; } 43 | } 44 | 45 | return NULL; 46 | } 47 | 48 | void 49 | mapremove(Map *map, 50 | Key k) 51 | { 52 | size_t h = map->hash(k) % map->nbuckets; 53 | Bucket *b, *p; 54 | 55 | for (p = NULL, b = map->buckets[h]; b; p = b, b = b->next) { 56 | if (map->cmp(k, b->k) == 0) { 57 | if (p) { 58 | p->next = b->next; 59 | } else { 60 | map->buckets[h] = b->next; 61 | } 62 | free(b); 63 | return; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /map.h: -------------------------------------------------------------------------------- 1 | #ifndef MAP_H 2 | #define MAP_H 3 | 4 | #include 5 | #include 6 | 7 | typedef union Key Key; 8 | typedef struct Bucket Bucket; 9 | typedef struct Map Map; 10 | 11 | union Key 12 | { 13 | int i; 14 | unsigned u; 15 | uint64_t u64; 16 | size_t sz; 17 | ssize_t ssz; 18 | void *ptr; 19 | }; 20 | 21 | struct Bucket 22 | { 23 | Bucket *next; 24 | 25 | Key k; 26 | char v[]; 27 | }; 28 | 29 | struct Map 30 | { 31 | size_t elemsz; 32 | size_t nbuckets; 33 | Bucket **buckets; 34 | size_t (*hash)(Key); 35 | int (*cmp)(Key, Key); 36 | }; 37 | 38 | int mapinit(Map *map, 39 | size_t elemsz, 40 | size_t nbuckets, 41 | size_t (*hash)(Key), 42 | int (*cmp)(Key, Key)); 43 | void *mapinsert(Map *map, 44 | Key k); 45 | void *mapfind(Map *map, 46 | Key k); 47 | void mapremove(Map *map, 48 | Key k); 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /nikolav2g.h: -------------------------------------------------------------------------------- 1 | #include "OpenV2G/v2gEXIDatatypes.h" 2 | #include "multitask.h" 3 | #include 4 | #include "polarssl/ctr_drbg.h" 5 | #include "polarssl/certs.h" 6 | #include "polarssl/x509.h" 7 | #include "polarssl/ssl.h" 8 | #include "polarssl/entropy.h" 9 | 10 | #ifndef _NIKOLAV2G_H 11 | #define _NIKOLAV2G_H 12 | 13 | // Verbose flag (set before running anything in this library): 14 | extern int chattyv2g; 15 | 16 | //============= 17 | // SDP 18 | //============= 19 | int ev_sdp_discover_evse(const char *if_name, 20 | struct sockaddr_in6 *evse_addr, 21 | bool tls_enabled); 22 | void sdp_listen(const char *if_name, int tls_port, int tcp_port); 23 | //============== 24 | // TLS 25 | //============== 26 | typedef int (*handle_func_t)(struct v2gEXIDocument*, 27 | struct v2gEXIDocument*, 28 | bool tls_enabled); 29 | void secc_listen_tls(int, handle_func_t, const char *crt_path, const char *key_path); 30 | void secc_listen_tcp(int, handle_func_t); 31 | int bind_v2gport(); 32 | typedef struct blocking_request blocking_request_t; 33 | 34 | // SECC connection Context that allows either for an fd or an ssl context 35 | 36 | typedef struct comboconn comboconn_t; 37 | struct comboconn{ 38 | bool tls_enabled; 39 | int sockfd; 40 | ssl_context ssl; 41 | 42 | }; 43 | 44 | typedef struct evcc_conn evcc_conn_t; 45 | struct evcc_conn{ 46 | bool alive; 47 | struct sockaddr_in6 addr; 48 | comboconn_t cconn; // Makes it possible for either TCP or TLS 49 | QLock mutex; 50 | Chan kill_chan; 51 | Chan kill_confirm_chan; 52 | // The connection keeps a queue of waiting requests to respond in correct order. 53 | blocking_request_t *first_req; 54 | blocking_request_t *last_req; 55 | 56 | // TLS Only stuff Stored here due to cleanup: 57 | x509_crt cacert; 58 | pk_context pkey; 59 | entropy_context entropy; 60 | ctr_drbg_context ctr_drbg; 61 | }; 62 | 63 | int evcc_connect_tls(evcc_conn_t *conn, const char *crt_path, const char *key_path); 64 | int evcc_connect_tcp(evcc_conn_t *conn); 65 | void evcc_close_conn (evcc_conn_t *conn); 66 | 67 | int v2g_request(evcc_conn_t *conn, struct v2gEXIDocument *exiIn, struct v2gEXIDocument *exiOut); 68 | 69 | 70 | //================== 71 | // Session 72 | //================== 73 | enum session_status { SESSION_ACTIVE, SESSION_PAUSED, SESSION_TERMINATED }; 74 | 75 | 76 | typedef struct secc_session session_t; 77 | struct secc_session{ 78 | uint64_t id; 79 | QLock mutex; 80 | int refcount; 81 | enum session_status status; 82 | void (*data_cleanup)(session_t *); 83 | void *data; 84 | // Cleanup function, autocalled upon destruction 85 | }; 86 | 87 | int gen_random_data(void *dest, size_t dest_len); 88 | int init_sessions(); 89 | session_t *session_new(size_t session_data_size, void (*data_cleanup)(session_t *)); 90 | session_t *session_lookup(uint64_t sessionid); 91 | session_t *session_lookup_exi(struct v2gEXIDocument *exiIn); 92 | void session_lock(session_t *session); 93 | void session_unlock(session_t *session); 94 | void session_terminate(session_t *session); 95 | void session_remove_ref(session_t *session); 96 | 97 | #endif 98 | -------------------------------------------------------------------------------- /sdp.c: -------------------------------------------------------------------------------- 1 | #include "nikolav2g.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #define SDP_ENFORCE_STRICT_SECURITY_REQUIREMENT 1 17 | #define SDP_VERSION 0x01 18 | #define SDP_INVERSE_VERSION 0xfe 19 | #define SDP_HEADER_LEN 8 20 | #define SDP_REQ_TYPE 0x9000 21 | #define SDP_RESP_TYPE 0x9001 22 | #define SDP_REQ_PAYLOAD_LEN 2 23 | #define SDP_RESP_PAYLOAD_LEN 20 24 | #define SDP_SRV_PORT 15118 25 | #define SDP_SECURITY_TLS 0x00 26 | #define SDP_SECURITY_NONE 0x10 27 | #define SDP_MAX_TRIES 50 28 | #define SDP_TRY_DELAY 250ULL //ms 29 | typedef uint8_t byte; 30 | 31 | #define TIME_MICROSECOND 1000 32 | #define TIME_MILLISECOND (TIME_MICROSECOND * 1000) 33 | #define TIME_SECOND (TIME_MILLISECOND * 1000) 34 | // ff::1 35 | static const uint8_t SDP_MULTICAST_ADDR[16] = {0xff, 0x02, 0, 0, 36 | 0, 0, 0, 0, 37 | 0, 0, 0, 0, 38 | 0, 0, 0, 1}; 39 | 40 | typedef unsigned long long uvlong; 41 | 42 | static inline uvlong nsleep(uvlong ns) 43 | { 44 | struct timespec left, ts = { .tv_sec = ns / TIME_SECOND, .tv_nsec = ns % TIME_SECOND }; 45 | int r = nanosleep(&ts, &left); 46 | assert(r == 0 || (r < 0 && errno == EINTR)); 47 | return (r == 0) ? 0 : ((uvlong)left.tv_sec * TIME_SECOND + (uvlong)ts.tv_nsec); 48 | } 49 | 50 | /*static void print_byte_arr(byte *arr, size_t n) 51 | { 52 | int i; 53 | if (chattyv2g) fprintf(stderr, "["); 54 | // Highly ineffictive but whatever it's TESTING!! :D 55 | for (i = 0; i < n; i++) { 56 | if (chattyv2g) fprintf(stderr, " %02x", arr[i]); 57 | } 58 | if (chattyv2g) fprintf(stderr, " ]\n"); 59 | }*/ 60 | 61 | static void write_header(byte *buf, 62 | uint16_t payload_type, 63 | uint32_t payload_len) 64 | { 65 | buf[0] = SDP_VERSION; // Version 66 | buf[1] = SDP_INVERSE_VERSION; // Inverse version 67 | buf[2] = (payload_type >> 8) & 0xff; // Payload type byte hi 68 | buf[3] = payload_type & 0xff; // Payload type lo 69 | buf[4] = (payload_len >> 24) & 0xff; // Payload length byte 1 (MSB) 70 | buf[5] = (payload_len >> 16) & 0xff; // Payload length byte 2 71 | buf[6] = (payload_len >> 8) & 0xff; // Payload length byte 3 72 | buf[7] = payload_len & 0xff; // Payload length part 4 (LSB) 73 | } 74 | 75 | static int validate_header(byte *buf, uint16_t expected_payload_type, 76 | uint32_t expected_payload_len) { 77 | uint16_t payload_type; 78 | uint32_t payload_len; 79 | if (buf[0] != SDP_VERSION) { 80 | if (chattyv2g) fprintf(stderr, "validate_header: invalid sdp version\n"); 81 | return -1; 82 | } 83 | if (buf[1] != SDP_INVERSE_VERSION) { 84 | if (chattyv2g) fprintf(stderr, "validate_header: invalid inverse sdp version\n"); 85 | return -1; 86 | } 87 | payload_type = (buf[2] << 8) + buf[3]; 88 | if (payload_type != expected_payload_type) { 89 | if (chattyv2g) fprintf(stderr, "validate_header: invalid payload type, expected %u, received %u\n", expected_payload_type, payload_type); 90 | return -1; 91 | } 92 | payload_len = (buf[4] << 24) + (buf[5] << 16) + (buf[6] << 8) + buf[7]; 93 | if (payload_len != expected_payload_len) { 94 | if (chattyv2g) fprintf(stderr, "validate_header: invalid payload length\n"); 95 | return -1; 96 | } 97 | return 0; 98 | } 99 | 100 | int get_interface_ipv6_address(const char *if_name, 101 | struct sockaddr_in6 *addr) 102 | { 103 | struct ifaddrs *ifa, *ifa_o; 104 | int err = getifaddrs(&ifa); 105 | if (err == -1) { 106 | return -1; 107 | } 108 | ifa_o = ifa; // original pointer, used for freeing 109 | // === Loop through all interface names and === 110 | // === find the corresponding address === 111 | for (; ifa != NULL; ifa = ifa->ifa_next) { 112 | if (ifa->ifa_addr != NULL && ifa->ifa_addr->sa_family == AF_INET6 113 | && strcmp(ifa->ifa_name, if_name) == 0) { 114 | memcpy(addr, (struct sockaddr_in6 *)(ifa->ifa_addr), sizeof(struct sockaddr_in6)); 115 | freeifaddrs(ifa_o); 116 | return 0; 117 | } 118 | } 119 | freeifaddrs(ifa_o); 120 | return -1; 121 | } 122 | 123 | //==================================== 124 | // EV (Client) 125 | //==================================== 126 | 127 | typedef struct ioargs ioargs_t; 128 | struct ioargs{ 129 | int sockfd; 130 | struct sockaddr_in6 *addr; 131 | byte security; 132 | }; 133 | 134 | // === Slave function for the SDP client === 135 | // === Attempts to send the SDP message SDP_MAX_TRIES (50) times 136 | // === using the multicast address provided on the provided socket=== 137 | static ssize_t request_writer(void *args, atomic_int *cancel) { 138 | ioargs_t *wargs = args; 139 | byte buf[SDP_HEADER_LEN+SDP_REQ_PAYLOAD_LEN]; 140 | byte *payload = buf + SDP_HEADER_LEN; 141 | ssize_t sentsz; 142 | int i = 0; 143 | byte security = wargs->security; 144 | // === Set Multicast Data === 145 | write_header(buf, SDP_REQ_TYPE, SDP_REQ_PAYLOAD_LEN); 146 | payload[0] = security; // TLS or TCP 147 | payload[1] = 0x00; // TCP = underlying protocol not matter what 148 | // Keep sending up to 50 multicast messages until cancelled 149 | while (i < SDP_MAX_TRIES && atomic_load(cancel) == 0) { 150 | if (chattyv2g) fprintf(stderr, "Broadcasting SDP multicast request, try %d\n", i+1); 151 | sentsz = sendto(wargs->sockfd, buf, 152 | SDP_HEADER_LEN + SDP_REQ_PAYLOAD_LEN, 153 | 0, (struct sockaddr *)wargs->addr, 154 | sizeof(struct sockaddr_in6)); 155 | if (sentsz != 8+SDP_REQ_PAYLOAD_LEN) { 156 | if (sentsz == -1) { 157 | if (chattyv2g) fprintf(stderr, "%s: %m\n", "sendto"); 158 | } 159 | return -1; 160 | } 161 | nsleep(SDP_TRY_DELAY * TIME_MILLISECOND); 162 | i++; 163 | } 164 | if (i == SDP_MAX_TRIES) { 165 | if (chattyv2g) fprintf(stderr, "Unable to find EVSE, stopping discovery\n"); 166 | } 167 | return 0; 168 | } 169 | 170 | // === Second slave function to the SDP client === 171 | // === Attempts to read unicast responses from an SDP server === 172 | static ssize_t response_reader(void *args, atomic_int *cancel) 173 | { 174 | ioargs_t *rargs = args; 175 | byte buf[512]; 176 | byte *payload = buf + SDP_HEADER_LEN; 177 | int err; 178 | ssize_t len; 179 | byte expected_secc_security = rargs->security; 180 | byte secc_security, secc_transport_protocol; 181 | while(atomic_load(cancel) == 0) { 182 | len = recv(rargs->sockfd, buf, SDP_HEADER_LEN+SDP_RESP_PAYLOAD_LEN, 0); 183 | if (len != SDP_HEADER_LEN + SDP_RESP_PAYLOAD_LEN) { 184 | if (len == -1) { 185 | if (chattyv2g) fprintf(stderr, "%s: %m\n", "recv"); 186 | return -1; 187 | } 188 | continue; 189 | } 190 | err = validate_header(buf, SDP_RESP_TYPE, SDP_RESP_PAYLOAD_LEN); 191 | if (err != 0) { 192 | if (chattyv2g) fprintf(stderr, "ev_sdp_resp_reader: validate_header error\n"); 193 | continue; 194 | } 195 | secc_security = payload[18]; 196 | if (secc_security != expected_secc_security) { 197 | if (chattyv2g) fprintf(stderr, "ev_sdp_resp_reader: evse does not support the chosen protocol, discarding\n"); 198 | continue; 199 | } 200 | secc_transport_protocol = payload[19]; 201 | if (secc_transport_protocol != 0x00) { 202 | if (chattyv2g) fprintf(stderr, "ev_sdp_resp_reader: evse does not support TCP as underlying transport, discarding\n"); 203 | continue; 204 | } 205 | break; 206 | } 207 | memcpy(rargs->addr->sin6_addr.s6_addr, payload, 16); 208 | memcpy(&rargs->addr->sin6_port, payload + 16, 2); 209 | if (chattyv2g) fprintf(stderr, "Succesful SDP response from EVSE\n"); 210 | return 0; 211 | } 212 | 213 | int ev_sdp_discover_evse(const char *if_name, 214 | struct sockaddr_in6 *evse_addr, 215 | bool tls_enabled) 216 | { 217 | int sock = 0, err = -1; 218 | ssize_t ret; 219 | struct sockaddr_in6 dest; 220 | Chan *iocr = iochan(1048576 - PTHREAD_STACK_MIN); 221 | Chan *iocw = iochan(1048576 - PTHREAD_STACK_MIN);; 222 | Alt alts[] = {{ .c = iocr, .v = &ret, .op = CHANRECV }, 223 | { .c = iocw, .v = &ret, .op = CHANRECV }, 224 | { .op = CHANEND }}; 225 | unsigned int if_index; 226 | ioargs_t rargs, wargs; 227 | if (iocr == NULL || iocw == NULL) { 228 | if (chattyv2g) fprintf(stderr, "slac_sendrecvloop: iochan error\n"); 229 | goto exit; 230 | } 231 | // === Get interface index === 232 | if_index = if_nametoindex(if_name); 233 | if (if_index == 0) { 234 | if (chattyv2g) fprintf(stderr, "%s: %m\n", "interface_index"); 235 | goto exit; 236 | } 237 | evse_addr->sin6_family = AF_INET6; 238 | evse_addr->sin6_scope_id = if_index; 239 | // === Set up socket === 240 | if (chattyv2g) fprintf(stderr, "Setting up socket\n"); 241 | sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); 242 | if (sock < 0) { 243 | goto exit; 244 | } 245 | // === Specify the socket used for multicast === 246 | err = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, &if_index, sizeof(if_index)); 247 | if (err < 0) { 248 | if (chattyv2g) fprintf(stderr, "%s: %m\n", "setsockopt"); 249 | goto exit; 250 | } 251 | // === Send the multicast message === 252 | memset((char *)&dest, 0, sizeof(dest)); 253 | dest.sin6_family = AF_INET6; 254 | dest.sin6_port = htons(SDP_SRV_PORT); 255 | memcpy(&dest.sin6_addr.s6_addr, SDP_MULTICAST_ADDR, 16); 256 | rargs.sockfd = sock; 257 | rargs.addr = evse_addr; 258 | rargs.security = tls_enabled ? SDP_SECURITY_TLS : SDP_SECURITY_NONE; 259 | wargs.sockfd = sock; 260 | wargs.addr = &dest; 261 | wargs.security = tls_enabled ? SDP_SECURITY_TLS : SDP_SECURITY_NONE; 262 | iocall(iocr, &response_reader, &rargs, sizeof(ioargs_t)); 263 | iocall(iocw, &request_writer, &wargs, sizeof(ioargs_t)); 264 | // === Receive responses from iocalls === 265 | // === If the send channel times out, no SDP server has responded in time === 266 | switch (alt(alts)) { 267 | case 0: // Done reading response 268 | iocancel(iocw); 269 | err = ret; 270 | break; 271 | case 1: // Done writing and no response -> error 272 | iocancel(iocr); 273 | err = -1; 274 | break; 275 | default: 276 | if (chattyv2g) fprintf(stderr, "critical ev_sdp_discover_evse: alt error\n"); 277 | abort(); 278 | } 279 | exit: 280 | if (iocr != NULL) { 281 | chanfree(iocr); 282 | } 283 | if (iocw != NULL) { 284 | chanfree(iocw); 285 | } 286 | if (sock > 0) { 287 | close(sock); 288 | } 289 | return err; 290 | } 291 | 292 | //================================================== 293 | // EVSE (server) 294 | //================================================== 295 | 296 | void evse_sdp_respond(const char *if_name, struct sockaddr_in6 raddr, 297 | uint16_t port, byte secc_security) 298 | { 299 | byte buf[SDP_HEADER_LEN+SDP_RESP_PAYLOAD_LEN]; 300 | ssize_t sentSz; 301 | byte *payload = buf + SDP_HEADER_LEN; 302 | struct sockaddr_in6 laddr; 303 | uint16_t port_bigendian = htons(port); 304 | // === Create ipv6 udp socket === 305 | int sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); 306 | if (sock < 0) { 307 | if (chattyv2g) fprintf(stderr, "%s: %m\n", "socket"); 308 | exit(-1); 309 | } 310 | if (get_interface_ipv6_address(if_name, &laddr)) { 311 | if (chattyv2g) fprintf(stderr, "%s: %m\n", "if_name_to_ipv6_addr"); 312 | return; 313 | } 314 | // === Write sdp response packet === 315 | write_header(buf, SDP_RESP_TYPE, SDP_RESP_PAYLOAD_LEN); 316 | memcpy(payload, laddr.sin6_addr.s6_addr, 16); 317 | memcpy(payload + 16, &port_bigendian, 2); 318 | payload[18] = secc_security; // Signal 0x01 for TCP only or 0x00 for TLS 319 | payload[19] = 0; // Set underlying protocol to TCP (no choice) 320 | // === Send sdp response packet === 321 | sentSz = sendto(sock, buf, 322 | SDP_HEADER_LEN+SDP_RESP_PAYLOAD_LEN, 0, 323 | (struct sockaddr *)&raddr, sizeof(struct sockaddr_in6)); 324 | if (sentSz < SDP_HEADER_LEN+SDP_RESP_PAYLOAD_LEN) { 325 | if (chattyv2g) fprintf(stderr, "%s: %m\n", "sendto"); 326 | } 327 | close(sock); 328 | } 329 | 330 | void sdp_listen(const char *if_name, int tls_port, int tcp_port) 331 | { 332 | struct sockaddr_in6 laddr = { 333 | .sin6_family = AF_INET6, 334 | .sin6_addr = in6addr_any, 335 | .sin6_port = htons(SDP_SRV_PORT), 336 | }; 337 | int sock, err, len; 338 | struct ipv6_mreq mreq; 339 | struct sockaddr_in6 raddr; 340 | size_t raddr_len = sizeof(raddr); 341 | if (chattyv2g) fprintf(stderr, "SDP is listening on interface %s\n", if_name); 342 | // === Get interface index === 343 | unsigned int if_index = if_nametoindex(if_name); 344 | if (if_index == 0) { 345 | if (chattyv2g) fprintf(stderr, "%s: %m\n", "interface_index"); 346 | exit(-1); 347 | } 348 | //sock = announce("udp![::1]:15118"); 349 | sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); 350 | if (sock < 0) { 351 | if (chattyv2g) fprintf(stderr, "%s: %m\n", "socket"); 352 | exit(-1); 353 | } 354 | // === Bind socket to SDP_SRV_PORT === 355 | err = bind(sock, (struct sockaddr *) &laddr, sizeof(laddr)); 356 | if (err != 0) { 357 | close(sock); 358 | if (chattyv2g) fprintf(stderr, "%s: %m\n", "bind"); 359 | exit(-1); 360 | } 361 | // === Join Multicast Group === 362 | memset(&mreq, 0, sizeof(mreq)); 363 | memcpy(&mreq.ipv6mr_multiaddr, 364 | SDP_MULTICAST_ADDR, 365 | sizeof(mreq.ipv6mr_multiaddr)); 366 | mreq.ipv6mr_interface = if_index; 367 | err = setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)); 368 | if (err != 0) { 369 | close(sock); 370 | if (chattyv2g) fprintf(stderr, "%s: %m\n", "IPV6_JOIN_GROUP"); 371 | exit(-1); 372 | } 373 | if (chattyv2g) fprintf(stderr, "SDP set to using TLS port = %d, TCP port = %d\n", tls_port, tcp_port); 374 | // === Keep receiving SDP requests === 375 | if (chattyv2g) fprintf(stderr, "Receive SDP requests\n"); 376 | for (;;) { 377 | byte buf[1024]; 378 | byte *payload = buf + SDP_HEADER_LEN; 379 | byte evcc_security; 380 | len = recvfrom(sock, buf, 1024, 0, 381 | (struct sockaddr *)&raddr, 382 | (socklen_t *)&raddr_len); 383 | if (len != SDP_HEADER_LEN + SDP_REQ_PAYLOAD_LEN) { 384 | if (len == -1) { 385 | if (chattyv2g) fprintf(stderr, "%s: %m\n", "recvfrom"); 386 | exit(-1); 387 | } 388 | if (chattyv2g) fprintf(stderr, "evse_sdp_listen_discovery_msg: invalid length\n"); 389 | continue; 390 | } 391 | err = validate_header(buf, SDP_REQ_TYPE, SDP_REQ_PAYLOAD_LEN); 392 | if (err != 0) { 393 | if (chattyv2g) fprintf(stderr, "evse_sdp_listen_discovery_msg: invalid header\n"); 394 | continue; 395 | } 396 | evcc_security = payload[0]; 397 | if (chattyv2g) fprintf(stderr, "SECC security = 0x%0x\n", evcc_security); 398 | if (evcc_security == SDP_SECURITY_TLS && tls_port > 0) { 399 | if (chattyv2g) fprintf(stderr, "Respond SDP with security field = TLS\n"); 400 | evse_sdp_respond(if_name, raddr, tls_port, SDP_SECURITY_TLS); 401 | } else if (tcp_port > 0) { 402 | if (chattyv2g) fprintf(stderr, "Respond SDP with security field = no security\n"); 403 | evse_sdp_respond(if_name, raddr, tcp_port, SDP_SECURITY_NONE); 404 | } 405 | } 406 | close(sock); 407 | } 408 | -------------------------------------------------------------------------------- /session.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "nikolav2g.h" 9 | #include "map.h" 10 | 11 | Map session_map; 12 | QLock session_map_mutex; 13 | 14 | static size_t hash(Key k) 15 | { 16 | return (size_t) k.u64 & 0xFFFFFFFF; 17 | } 18 | 19 | static int cmp(Key k1, Key k2) 20 | { 21 | if (k1.u64 == k2.u64) { 22 | return 0; 23 | } 24 | return -1; 25 | } 26 | 27 | int gen_random_data(void *dest, size_t dest_len) 28 | { 29 | int fd = open("/dev/urandom", O_RDONLY); 30 | int len = 0; 31 | if (fd == -1) { 32 | if (chattyv2g) fprintf(stderr, "%s: %m\n", "gen_random_data: open /dev/urandom"); 33 | return -1; 34 | } 35 | while (len < dest_len) { 36 | ssize_t result = read(fd, (char*)dest + len, dest_len - len); 37 | if (result < 0) { 38 | if (chattyv2g) fprintf(stderr, "%s: %m\n", "gen_random_data: read"); 39 | close(fd); 40 | return -1; 41 | } 42 | len += result; 43 | } 44 | close(fd); 45 | return 0; 46 | } 47 | 48 | void session_lock(session_t *session) 49 | { 50 | if (session == NULL) { 51 | return; 52 | } 53 | qlock(&session->mutex); 54 | } 55 | void session_unlock(session_t *session) 56 | { 57 | if (session == NULL) { 58 | return; 59 | } 60 | qunlock(&session->mutex); 61 | } 62 | 63 | int init_sessions()// 64 | { 65 | int err = mapinit(&session_map, 66 | sizeof(session_t*), 67 | 256, 68 | &hash, 69 | &cmp); 70 | if (err == -1) { 71 | printf("main: map_create failed\n"); 72 | return -1; 73 | } 74 | memset(&session_map_mutex, 0, sizeof(session_map_mutex)); 75 | return 0; 76 | } 77 | 78 | session_t *session_lookup(uint64_t sessionid) 79 | { 80 | if (sessionid == 0) { 81 | return NULL; 82 | } 83 | union Key k = {.u64 = sessionid}; 84 | qlock(&session_map_mutex); 85 | session_t **sessionpp = (session_t**)mapfind(&session_map, k); 86 | if (sessionpp == NULL) { 87 | printf("session_lookup: invalid session\n"); 88 | return NULL; 89 | } 90 | session_lock(*sessionpp); 91 | (*sessionpp)->refcount++; 92 | if ((*sessionpp)->refcount > 4) { 93 | printf("session_lookup: %d references to the same session, code error is likely\n", (*sessionpp)->refcount); 94 | } 95 | session_unlock(*sessionpp); 96 | qunlock(&session_map_mutex); 97 | 98 | return *sessionpp; 99 | } 100 | 101 | session_t *session_new(size_t session_data_size, void (*data_cleanup)(session_t *)) 102 | { 103 | union Key k; 104 | // Must be hex binary 105 | int err = gen_random_data(&k.u64, 8); 106 | if (err != 0) { 107 | return NULL; 108 | } 109 | qlock(&session_map_mutex); 110 | session_t **sessionpp = (session_t**)mapinsert(&session_map, k); 111 | if (sessionpp == NULL) { 112 | return NULL; 113 | } 114 | *sessionpp = malloc(sizeof(session_t) + session_data_size); 115 | if (*sessionpp == NULL) { 116 | return NULL; 117 | } 118 | memset(*sessionpp, 0, sizeof(session_t) + session_data_size); 119 | qunlock(&session_map_mutex); 120 | //(*sessionpp)->tls_enabled = tls_enabled; 121 | (*sessionpp)->id = k.u64; 122 | (*sessionpp)->status = SESSION_ACTIVE; 123 | (*sessionpp)->refcount = 1; 124 | (*sessionpp)->data_cleanup = data_cleanup; 125 | return *sessionpp; 126 | } 127 | 128 | 129 | session_t *session_lookup_exi(struct v2gEXIDocument *exiIn) 130 | { 131 | uint64_t sessionid; 132 | memcpy(&sessionid, exiIn->V2G_Message.Header.SessionID.bytes, 8); 133 | return session_lookup(sessionid); 134 | } 135 | 136 | 137 | void session_terminate(session_t *session) 138 | { 139 | union Key k = { .u64 = session->id }; 140 | qlock(&session_map_mutex); 141 | mapremove(&session_map, k); 142 | session->status = SESSION_TERMINATED; 143 | qunlock(&session_map_mutex); 144 | } 145 | 146 | void session_pause(session_t *session, bool pause) 147 | { 148 | session->status = SESSION_PAUSED; 149 | } 150 | 151 | void session_remove_ref(session_t *session) 152 | { 153 | if (session == NULL) { 154 | return; 155 | } 156 | session_lock(session); 157 | session->refcount--; 158 | if (session->refcount == 0 && session->status == SESSION_TERMINATED) { 159 | if (session->data_cleanup != NULL) { 160 | session->data_cleanup(session); 161 | } 162 | free(session); 163 | if (chattyv2g) fprintf(stderr, "Succesfully freed session\n"); 164 | // No need to unlock if refcount 0, since it can never increase from this point since it has been removed from the map. 165 | } else if (session->refcount < 0) { 166 | if (chattyv2g) fprintf(stderr, "session_remove_ref: Negative session ref-count. THIS IS BAD!\n"); 167 | } else { 168 | session_unlock(session); 169 | } 170 | } 171 | --------------------------------------------------------------------------------