diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000..b60b1f102 --- /dev/null +++ b/.clang-format @@ -0,0 +1,95 @@ +--- +Language: Cpp +# BasedOnStyle: Google +AccessModifierOffset: -1 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlinesLeft: true +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: false +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: All +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: true +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 120 +CommentPragmas: '^ IWYU pragma:' +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: true +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] +IncludeCategories: + - Regex: '^<.*\.h>' + Priority: 1 + - Regex: '^<.*' + Priority: 2 + - Regex: '.*' + Priority: 3 +IncludeIsMainRegex: '([-_](test|unittest))?$' +IndentCaseLabels: true +IndentWidth: 8 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: false +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Left +ReflowComments: true +SortIncludes: true +SpaceAfterCStyleCast: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Auto +TabWidth: 8 +UseTab: Never +... + diff --git a/CPPLINT.cfg b/CPPLINT.cfg index 41265bb6e..36c3851ea 100644 --- a/CPPLINT.cfg +++ b/CPPLINT.cfg @@ -1 +1 @@ -linelength=100 +linelength=120 diff --git a/ci/.gitignore b/ci/.gitignore index 594e0099f..100ae87b0 100644 --- a/ci/.gitignore +++ b/ci/.gitignore @@ -1,4 +1,9 @@ +access_log config +webhook-config.json +encrypted_secret.bin +private.pem +public.pem githubcreds mykeyfile repository diff --git a/ci/README.md b/ci/README.md index 9415feca0..e490ea5d2 100644 --- a/ci/README.md +++ b/ci/README.md @@ -3,12 +3,12 @@ ### Setting up CI Run a Flask server that listens for new events from github, will get triggered when a new PR is created or when keyword `@onvm` is mentioned. ```sh -python3 webhook-receiver.py 0.0.0.0 8080 @onvm +python3 webhook-receiver.py 0.0.0.0 8080 @onvm webhook-config.json ``` To run CI tests manually, requires a config file, the github PR ID, request message and a response message. ```sh -./manager.sh +./manager.sh ``` ### Usage @@ -34,7 +34,6 @@ The CI process can be broken into multiple steps: WORKER_LIST=("WORKER_1_IP WORKER_1_KEY", "WORKER_2_IP WORKER_2_KEY", ...) GITHUB_CREDS=path_to_creditential_file REPO_OWNER="OWNER_STRING" - REPO_NAME="NAME_STRING" ``` Config file example: @@ -42,7 +41,16 @@ The CI process can be broken into multiple steps: WORKER_LIST=("nimbnode42 nn42_key") GITHUB_CREDS=githubcreds REPO_OWNER="sdnfv" - REPO_NAME="openNetVM-dev" + ``` + + Webhook json config example + ``` + { + "secret-file": "very_special_encrypted_secret_file.bin", + "private-key-file": "private_key.pem", + "log-successful-attempts": true, + "authorized-users": ["puffin", "penguin", "pcoach"] + } ``` GITHUB_CREDS file example: diff --git a/ci/ci_busy.sh b/ci/ci_busy.sh index 1dd1e732a..59d510f39 100755 --- a/ci/ci_busy.sh +++ b/ci/ci_busy.sh @@ -29,18 +29,26 @@ fi if [[ -z "$3" ]] then - echo "ERROR: Missing third argument, Request body!" + echo "ERROR: Missing third argument, Repo name!" exit 1 else - REQUEST=$3 + REPO_NAME=$3 fi if [[ -z "$4" ]] then - echo "ERROR: Missing fourth argument, POST_MSG!" + echo "ERROR: Missing fourth argument, Request body!" exit 1 else - POST_MSG=$4 + REQUEST=$4 +fi + +if [[ -z "$5" ]] +then + echo "ERROR: Missing fifth argument, POST_MSG!" + exit 1 +else + POST_MSG=$5 fi . $1 # source the variables from config file @@ -48,24 +56,18 @@ fi print_header "Checking Required Variables" - if [[ -z "$GITHUB_CREDS" ]] +if [[ -z "$GITHUB_CREDS" ]] then echo "ERROR: GITHUB_CREDS not provided" exit 1 fi - if [[ -z "$REPO_OWNER" ]] +if [[ -z "$REPO_OWNER" ]] then echo "ERROR: REPO_OWNER not provided" exit 1 fi - if [[ -z "$REPO_NAME" ]] -then - echo "ERROR: REPO_NAME not provided" - exit 1 -fi - print_header "Posting Message in Comments on GitHub" python3 post-msg.py $GITHUB_CREDS "{\"id\": $PR_ID,\"request\":\"$REQUEST\"}" $REPO_OWNER $REPO_NAME "$POST_MSG" check_exit_code "ERROR: Failed to post results to GitHub" diff --git a/ci/clone-and-checkout-pr.py b/ci/clone-and-checkout-pr.py index e55ea9a16..28cd68ae8 100644 --- a/ci/clone-and-checkout-pr.py +++ b/ci/clone-and-checkout-pr.py @@ -41,10 +41,17 @@ cmd = "git clone " + str(repo.clone_url) + " repository" child = pexpect.spawn(cmd) -child.expect("Username.*") -child.sendline(username + "\n") -child.expect("Password.*") -child.sendline(password + "\n") + +if '-dev' in REPO_NAME: + child.expect("Username.*") + child.sendline(username + "\n") + child.expect("Password.*") + child.sendline(password + "\n") + child.interact() +# Get the latest branches for upstream (used for proper linter check) +pexpect.run("git remote add upstream https://github.com/sdnfv/openNetVM.git", cwd="./repository") +pexpect.run("git fetch upstream", cwd="./repository") + print(pexpect.run("git checkout " + branch_name, cwd="./repository")) diff --git a/ci/helper-functions.sh b/ci/helper-functions.sh index 0d2039aa1..6a269e54e 100644 --- a/ci/helper-functions.sh +++ b/ci/helper-functions.sh @@ -87,7 +87,7 @@ obtain_core_config() { # checks if a command has failed (exited with code != 0) # if it does, print the error message, exit the build, and post to github -check_exit_code(){ +check_exit_code() { if [ $? -ne 0 ] then echo $1 @@ -99,7 +99,46 @@ check_exit_code(){ # runs the linter run_linter() { - for fn in $(git ls-files *.c | grep -v "cJSON" | grep -v "ndpi"); do - python style/gwclint.py --verbose=5 $fn 2>&1 | grep -v "Done" | grep -v "Total errors found: 0" >> $1 + for fn in $(git diff --name-only upstream/develop...HEAD -- '*.c' '*.cpp' '*.h' | grep -v "cJSON" | grep -v "ndpi"); do + git_file_diff=$(git diff -U0 upstream/develop...HEAD -- $fn) + file_modifications=() + while read -r diff_line; do + # line number is denoted by @@ -lines_deleted +lines_added @@ + # For example: @@ -1257,3 +1175,3 @@ onvm_nflib_cleanup(struct onvm_nf_context *nf_context) { + # We're interested in 1175, 3 as that shows the lines in the final file + if [[ $diff_line == @@* ]]; then + diff_line=$(cut -d ' ' -f 3 <<< "$diff_line") + diff_line=${diff_line:1} + line_start=$(cut -d ',' -f 1 <<< "$diff_line") + line_end=$(cut -d ',' -f 2 <<< "$diff_line") + # Expand the comment on one line diff vs multiline->therefore addition + [[ ! $line_end == $line_start ]] && line_end=$(($line_start + $line_end)) + file_modifications[$line_start]=$line_end + # create mapping of start of chunk -> end line + fi + done <<< "$git_file_diff" + + # loop through the lines changed in the current file + file_lint=$(python $SCRIPT_LOC/../style/gwclint.py --verbose=4 $fn 2>&1 | grep -v "Done" | grep -v "Total errors found:") + errors_found=0 + while read -r line; do + error_line=$(cut -d ':' -f 2 <<< "$line") + # lint error is denoted by *:line:* + for start in "${!file_modifications[@]}"; do + # loop through the git diff line numbers + end=${file_modifications[$start]} + if [[ $error_line -ge $start && $error_line -le $end ]]; then + # git diff and linter output are in range, add to lint file + errors_found=$(($errors_found+1)) + echo "$line" >> $1 + break + fi + done + done <<< "$file_lint" + # loop through the linter output + if [[ $errors_found -gt 0 ]]; then + # only output to file if we had errors + echo "Total errors found: $errors_found" >> $1 + fi done } diff --git a/ci/manager.sh b/ci/manager.sh index d09b2d612..a4b9bbd96 100755 --- a/ci/manager.sh +++ b/ci/manager.sh @@ -31,10 +31,18 @@ fi if [[ -z "$3" ]] then - echo "ERROR: Missing third argument, Request body!" + echo "ERROR: Missing third argument, Repo name!" exit 1 else - REQUEST=$3 + REPO_NAME=$3 +fi + +if [[ -z "$4" ]] +then + echo "ERROR: Missing fourth argument, Request body!" + exit 1 +else + REQUEST=$4 fi . $1 # source the variables from config file @@ -60,12 +68,6 @@ then exit 1 fi -if [[ -z "$REPO_NAME" ]] -then - echo "ERROR: REPO_NAME not provided" - exit 1 -fi - print_header "Cleaning up Old Results" sudo rm -f *.txt @@ -188,13 +190,13 @@ do worker_key_file="${tuple_arr[1]}" scp -i $worker_key_file -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null $worker_ip:stats ./$worker_ip.stats check_exit_code "ERROR: Failed to fetch results from $worker_ip" - echo "[Results from $worker_ip]" >> results_summary.stats - python3 speed-tester-analysis.py ./$worker_ip.stats >> results_summary.stats + # TODO: this will overwrite results if we have more than 1 worker, investigate this case + python3 speed-tester-analysis.py ./$worker_ip.stats $worker_ip results_summary.stats check_exit_code "ERROR: Failed to analyze results from $worker_ip" done print_header "Posting Results in Comment on GitHub" -python3 post-msg.py $GITHUB_CREDS "{\"id\": $PR_ID,\"request\":\"$REQUEST\",\"linter\": 1,\"results\": 1}" $REPO_OWNER $REPO_NAME "Run successful see results:" +python3 post-msg.py $GITHUB_CREDS "{\"id\": $PR_ID,\"request\":\"$REQUEST\",\"linter\": 1,\"results\": 1,\"review\": 1}" $REPO_OWNER $REPO_NAME "Run successful see results:" check_exit_code "ERROR: Failed to post results to GitHub" print_header "Finished Executing" diff --git a/ci/post-msg.py b/ci/post-msg.py index 2cd00d711..3c0f8771d 100644 --- a/ci/post-msg.py +++ b/ci/post-msg.py @@ -7,6 +7,8 @@ REPO_OWNER = None REPO_NAME = None +BASE_BRANCH = 'develop' +ACTION = 'APPROVE' if len(sys.argv) != 6: print("ERROR! Incorrect number of arguments") @@ -26,6 +28,7 @@ REQUEST = data['request'] POST_LINTER_OUTPUT = 'linter' in data POST_RESULTS = 'results' in data +POST_REVIEW = 'review' in data REPO_OWNER = str(sys.argv[3]) REPO_NAME = str(sys.argv[4]) @@ -37,8 +40,8 @@ print("ERROR: Could not authenticate with GitHub!") sys.exit(1) -issue = gh.issue(REPO_OWNER, REPO_NAME, PR_ID) -if issue is None or str(issue) is "": +pull_request = gh.pull_request(REPO_OWNER, REPO_NAME, PR_ID) +if pull_request is None or str(pull_request) is "": print("ERROR: Could not get PR information from GitHub for PR %d" % int(sys.argv[2])) sys.exit(1) @@ -48,12 +51,25 @@ comment_body += "\n## CI Message\n\n" comment_body += POST_MSG + "\n" -if POST_RESULTS: - with open("./results_summary.stats", "r") as f: - numerical_results = f.read().strip() - comment_body += numerical_results +if POST_REVIEW: + # PR must be submitted to develop branch + base_branch_name = pull_request.base.label.split(':')[1].strip() + if (base_branch_name == BASE_BRANCH): + comment_body += " :heavy_check_mark: PR submitted to **_%s_** branch\n" % base_branch_name + else: + comment_body += " :x: FIX PR submitted to **_%s_** branch instead of **_%s_**\n" % (base_branch_name, BASE_BRANCH) + ACTION = 'REQUEST_CHANGES' + + # PR must not affect performance + with open('./results_summary.stats') as f: + results = json.load(f) + if (results['pass_performance_check']): + comment_body += " :heavy_check_mark: Speed tester performance check passed\n" + else: + comment_body += " :x: PR drops speed tester perforamce bellow minimum requirement\n" + ACTION = 'REQUEST_CHANGES' -if POST_LINTER_OUTPUT: + # PR must pass linter check linter_output = None try: with open("./linter-output.txt", "r") as f: @@ -62,8 +78,33 @@ pass if linter_output is not None and linter_output != "": - comment_body += ("\n\n## Linter Failed\n\n" + linter_output) + comment_body += " :x: Linter Failed (please fix style errors)\n" + ACTION = 'REQUEST_CHANGES' else: - comment_body += ("\n\n## Linter Passed") + comment_body += " :heavy_check_mark: Linter passed\n" -issue.create_comment(comment_body) +if POST_RESULTS: + with open('./results_summary.stats') as f: + results = json.load(f) + comment_body += "\n " + results['summary'] + +if POST_LINTER_OUTPUT: + linter_output = None + try: + with open("./linter-output.txt", "r") as f: + linter_output = f.read().strip() + except: + pass + + if linter_output is not None and linter_output != "": + comment_body += ("\n\n## Linter Output\n\n" + linter_output) + +if POST_REVIEW: + # Actual review is required + pull_request.create_review( + body=comment_body, + event=ACTION + ) +else: + # Just a general info comment + pull_request.create_comment(comment_body) diff --git a/ci/speed-tester-analysis.py b/ci/speed-tester-analysis.py index 39476a016..8d34194f7 100644 --- a/ci/speed-tester-analysis.py +++ b/ci/speed-tester-analysis.py @@ -3,6 +3,8 @@ import json import re +AVG_SPEED = 35000000 + """ get median value of tx pps """ @@ -14,15 +16,32 @@ def median(array): else: return (array[half - 1] + array[half]) / 2.0 -if(len(sys.argv) != 2): +if(len(sys.argv) != 4): print("ERROR: Invalid arguments.") sys.exit(1) -with open(sys.argv[1], "r") as f: +STATS_FILE = sys.argv[1] +STATS_NODE_NAME = sys.argv[2] +OUT_FILE = sys.argv[3] + +with open(STATS_FILE, "r") as f: contents = f.read() pattern = re.compile("TX pkts per second:\s*([0-9]*)") data = [int(x) for x in pattern.findall(contents)] +median_speed = median(data) + +results = {} +results['node'] = STATS_NODE_NAME +results['speed'] = median_speed + +performance_rating = (median_speed / AVG_SPEED) * 100 +if (performance_rating < 97): + results['pass_performance_check'] = False +else: + results['pass_performance_check'] = True +results['performance_rating'] = performance_rating +results['summary'] = "[Results from %s]\n - Median TX pps for Speed Tester: %d\n - Performance rating - %.2f%% (compared to %d average)" % (STATS_NODE_NAME, median_speed, performance_rating, AVG_SPEED) -summary = "Median TX pps for Speed Tester: %d" % median(data) -print(summary) +with open(OUT_FILE, 'w') as outfile: + json.dump(results, outfile) diff --git a/ci/webhook-receiver.py b/ci/webhook-receiver.py index 9739918c8..e6230b6e4 100644 --- a/ci/webhook-receiver.py +++ b/ci/webhook-receiver.py @@ -1,18 +1,108 @@ from flask import Flask, jsonify, request + import requests +from ipaddress import ip_address, ip_network + +from Crypto.PublicKey import RSA +from Crypto.Cipher import AES, PKCS1_OAEP +import hmac import json import sys import pprint import os import subprocess +import logging +# Global vars EVENT_URL = "/github-webhook" -CI_NAME="onvm" +CI_NAME = "onvm" +KEYWORD = None +access_log_enabled = None +authorized_users = None +secret_file_name = None +private_key_file = None +secret = None app = Flask(__name__) -# returns extracted data if it is an event for a PR creation or PR comment creation +logging.getLogger('werkzeug').setLevel(logging.ERROR) +logging.basicConfig(filename="access_log", filemode='a', + format='%(asctime)s, %(name)s %(levelname)s %(message)s', + datefmt='%d-%b-%y %H:%M:%S', level=logging.INFO) + +def get_request_info(request_ctx): + return "Request details: IP: {}, User: {}, Repo: {}, ID: {}, Body: {}.".format(request_ctx['src_ip'], request_ctx['user'], request_ctx['repo'], request_ctx['id'], request_ctx['body']) + +def log_access_granted(request_ctx, custom_msg): + if (access_log_enabled): + logging.info("Access GRANTED: {}. {}".format(custom_msg, get_request_info(request_ctx))) + +def log_access_denied(request_ctx, custom_msg): + logging.warning("Access DENIED: {}. {}".format(custom_msg, get_request_info(request_ctx))) + +def decrypt_secret(): + global secret_file_name + global private_key_file_name + + secret_file = open(secret_file_name, "rb") + private_key = RSA.import_key(open(private_key_file_name).read()) + + enc_session_key, nonce, tag, ciphertext = \ + [ secret_file.read(x) for x in (private_key.size_in_bytes(), 16, 16, -1) ] + + # Decrypt the session key with the private RSA key + cipher_rsa = PKCS1_OAEP.new(private_key) + session_key = cipher_rsa.decrypt(enc_session_key) + + # Decrypt the data with the AES session key + cipher_aes = AES.new(session_key, AES.MODE_EAX, nonce) + data = cipher_aes.decrypt_and_verify(ciphertext, tag) + + # Clear memory + secret_file = private_key = None + private_key_file_name = secret_file_name = None + enc_session_key = nonce = tag = ciphertext = None + del private_key_file_name + del secret_file_name + del secret_file + del private_key + del enc_session_key + del nonce + del tag + del ciphertext + + return data + +def verify_request_ip(request_ctx): + src_ip = request_ctx['src_ip'] + valid_ips = requests.get('https://api.github.com/meta').json()['hooks'] + + for ip in valid_ips: + if src_ip in ip_network(ip): + return True + + return False + +def verify_request_secret(request_ctx): + global secret + header_signature = request_ctx['X-Hub-Signature'] + if header_signature is None: + return False + + signature = header_signature.split('=')[1] + + mac = hmac.new(secret, msg=request_ctx['data'], digestmod='sha1') + secret_comparison = hmac.compare_digest(mac.hexdigest(), signature) + + # Memory cleanup + header_signature = signature = None + del header_signature + del signature + + return secret_comparison + +# Returns extracted data if it is an event for a PR creation or PR comment creation # if it is a PR comment, only return extracted data if it contains the required keyword specified by the global var # if it doesn't contain the keyword or is not the correct type of event, return None def filter_to_prs_and_pr_comments(json): @@ -20,8 +110,14 @@ def filter_to_prs_and_pr_comments(json): if json is None: return None + if 'action' not in json: + return None + if json['action'] == 'opened' and 'pull_request' in json and 'base' in json['pull_request']: branch_name = json['pull_request']['base']['label'] + repo_name = json['repository']['name'] + user_name = json['pull_request']['user']['login'] + if branch_name is None: return None @@ -32,13 +128,17 @@ def filter_to_prs_and_pr_comments(json): return { "id": number, + "repo": repo_name, "branch": branch_name, + "user": user_name, "body": "In response to PR creation" } if json['action'] == 'created' and 'issue' in json and json['issue']['state'] == 'open' and 'pull_request' in json['issue'] and 'comment' in json: - + repo_name = json['repository']['name'] comment_txt = json['comment']['body'] + user_name = json['comment']['user']['login'] + if KEYWORD not in comment_txt: return None if json['sender']['login'] == CI_NAME: @@ -51,6 +151,8 @@ def filter_to_prs_and_pr_comments(json): return { "id": number, + "repo": repo_name, + "user": user_name, "body": comment_txt } @@ -58,47 +160,99 @@ def filter_to_prs_and_pr_comments(json): @app.route(EVENT_URL, methods=['POST']) def init_ci_pipeline(): + request_ctx = filter_to_prs_and_pr_comments(request.json) + if request_ctx is None: + logging.debug("Request filter doesn't match request") + return jsonify({"success": True}) + + request_ctx['src_ip'] = ip_address(u'{}'.format(request.access_route[0])) + request_ctx['X-Hub-Signature'] = request.headers.get('X-Hub-Signature') + request_ctx['data'] = request.data + + if not verify_request_ip(request_ctx): + print("Incoming webhook not from a valid Github address") + log_access_denied(request_ctx, "Incoming webhook not from a valid Github address") + return jsonify({"success": True}) + + if not verify_request_secret(request_ctx): + print("Incoming webhook secret doesn't match configured secret") + log_access_denied(request_ctx, "Incoming webhook has an invalid secret") + return jsonify({"success": True}) - extracted_data = filter_to_prs_and_pr_comments(request.json) - if extracted_data is not None: - print("Data matches filter, we should RUN CI") - print(extracted_data) - - # Check if there is another CI run in progress - proc1 = subprocess.Popen(['ps', 'cax'], stdout=subprocess.PIPE) - proc2 = subprocess.Popen(['grep', 'manager.sh'], stdin=proc1.stdout, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - proc1.stdout.close() - out, err = proc2.communicate() - - if (out): - print("Can't run CI, another CI run in progress") - os.system("./ci_busy.sh config {} \"{}\" \"Another CI run in progress, please try again in 15 minutes\"" - .format(extracted_data['id'], extracted_data['body'])) - else: - os.system("./manager.sh config {} \"{}\"".format(extracted_data['id'], extracted_data['body'])) + if (request_ctx['repo'] == 'openNetVM' and request_ctx['user'] not in authorized_users): + print("Incoming request is from an unathorized user") + log_access_denied(request_ctx, "Incoming request is from an unathorized user") + os.system("./ci_busy.sh config {} \"{}\" \"{}\" \"User not authorized to run CI, please contact one of the repo maintainers\"" + .format(request_ctx['id'], request_ctx['repo'], request_ctx['body'])) + return jsonify({"success": True}) + + print("Request matches filter, we should RUN CI. {}".format(get_request_info(request_ctx))) + + # Check if there is another CI run in progress + proc1 = subprocess.Popen(['ps', 'cax'], stdout=subprocess.PIPE) + proc2 = subprocess.Popen(['grep', 'manager.sh'], stdin=proc1.stdout, + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + proc1.stdout.close() + out, err = proc2.communicate() + + if (out): + print("Can't run CI, another CI run in progress") + log_access_granted(request_ctx, "CI busy, posting busy msg") + os.system("./ci_busy.sh config {} \"{}\" \"{}\" \"Another CI run in progress, please try again in 15 minutes\"" + .format(request_ctx['id'], request_ctx['repo'], request_ctx['body'])) else: - print("Data did not match filter, SKIP CI") + log_access_granted(request_ctx, "Running CI") + os.system("./manager.sh config {} \"{}\" \"{}\"".format(request_ctx['id'], request_ctx['repo'], request_ctx['body'])) - return jsonify({ - "success": True - }) + return jsonify({"status": "ONLINE"}) @app.route("/status", methods=['GET']) def status(): - return jsonify({ - "status": "ONLINE" - }) + return jsonify({"status": "ONLINE"}) -if __name__ == "__main__": - global KEYWORD +def parse_config(cfg_name): + global access_log_enabled + global secret_file_name + global private_key_file_name + global authorized_users + + with open (cfg_name, 'r') as cfg: + webhook_config = json.load(cfg) - if(len(sys.argv) != 4): + access_log_enabled = webhook_config['log-successful-attempts'] + if access_log_enabled is None: + print("Access log switch not specified in the webhook server config") + sys.exit(1) + + secret_file_name = webhook_config['secret-file'] + if secret_file_name is None: + print("No secret file found in the webhook server config") + sys.exit(1) + + private_key_file_name = webhook_config['private-key-file'] + if private_key_file_name is None: + print("No private key file found in the webhook server config") + sys.exit(1) + + authorized_users = webhook_config['authorized-users'] + if authorized_users is None: + print("No authroized users found in webhook server config") + sys.exit(1) + + +if __name__ == "__main__": + if(len(sys.argv) != 5): print("Invalid arguments!") sys.exit(1) host = sys.argv[1] port = sys.argv[2] KEYWORD = sys.argv[3] + cfg_name = sys.argv[4] + + parse_config(cfg_name) + + secret = decrypt_secret() + logging.info("Starting the CI service") app.run(host=host, port=port) diff --git a/ci/worker.sh b/ci/worker.sh index c4f0ada40..c49baae9d 100755 --- a/ci/worker.sh +++ b/ci/worker.sh @@ -24,7 +24,7 @@ check_exit_code "ERROR: Building ONVM failed" print_header "Running ONVM Manager" cd onvm -./go.sh 0,1,2 0 0xF0 -s web & +./go.sh 0,1,2,3 0 0xF0 -s web -a 0x7f000000000 & mgr_pid=$? if [ $mgr_pid -ne 0 ] then diff --git a/docs/Contributing.md b/docs/Contributing.md index 8adf763f5..ca7f6b547 100644 --- a/docs/Contributing.md +++ b/docs/Contributing.md @@ -16,6 +16,6 @@ To contribute to OpenNetVM, please follow these steps: - `git pull --rebase upstream develop` 8. Please fill out the pull request template as best as possible and be very detailed/thorough. -[style]: style/styleguide.md +[style]: ../style/styleguide.md [gitflow]: https://guides.github.com/introduction/flow/ [commitguide]: https://chris.beams.io/posts/git-commit/ diff --git a/docs/Debug.md b/docs/Debug.md index f7582232b..74a821808 100644 --- a/docs/Debug.md +++ b/docs/Debug.md @@ -18,6 +18,14 @@ For example, you may want to debug [speed tester][speed_tester] with gdb. + `sudo gdb --args ./build/speed_tester -l 5 -n 3 --proc-type=secondary -- -r 1 -- -d 1` - It is now possible to set breakpoints and perform other gdb operations! +**Troubleshooting:** +If debugging symbols are not found verify that debugging flags are set with `echo $USER_FLAGS` and also try executing the following command in gdb: + - For onvm_mgr: + `file onvm_mgr/x86_64-native-linuxapp-gcc/app/onvm_mgr` + + - For NFs: + `file build/app/NF_NAME` + If for some reason `USER_FLAGS` does not set correctly, it's possible to edit the `Makefile` of your debug target and set the flags manually. It can be done by adding a line similar to this: - `CFLAGS += -g` diff --git a/docs/Install.md b/docs/Install.md index e2493c46c..c09783722 100644 --- a/docs/Install.md +++ b/docs/Install.md @@ -21,7 +21,7 @@ Check System ```sh sudo apt-get install build-essential linux-headers-$(uname -r) git ``` -4. Assure your kernel suppors uio +4. Assure your kernel supports uio ```sh locate uio ``` @@ -29,6 +29,10 @@ Check System ```sh sudo apt-get install libnuma-dev ``` + If installing libnuma-dev fails, your system may not be up to date. To fix this, run: + ```sh + sudo apt-get update + ``` Setup Repositories -- @@ -178,8 +182,27 @@ Also, please double check if the environment variables from [step 3](#3-set-up-e Troubleshooting -- +1. **Setting up DPDK manually** + + Our install script helps configure DPDK by using its setup script. Sometimes, it's helpful to troubleshoot a problem by running DPDK's script directly to fix things like misconfigured igb_uio drivers or hugepage regions. + + Here are the steps used to install the DPDK components needed by ONVM. + + Run `dpdk/usertools/dpdk-setup.sh` then: + + - Press [15] to compile x86_64-native-linuxapp-gcc version -1. **Huge Page Configuration** + - Press [18] to install igb_uio driver for Intel NICs + + - Press [22] to setup 1024 2MB hugepages + + - Press [24] to register the Ethernet ports + + - Press [35] to quit the tool + + After these steps, it should be possible to compile and run onvm. + +2. **Huge Page Configuration** You can get information about the hugepage configuration with: @@ -191,14 +214,15 @@ Troubleshooting - In this case, either kill them manually by hitting Ctrl+C or run `sudo pkill NF_NAME` for every NF that you have ran. - The manager and NFs are not running, but something crashed without freeing hugepages. - To fix this, please run `sudo rm -rf /mnt/huge/*` to remove all files that contain hugepage data. + - The above two cases are not met, something weird is happening: - A reboot might fix this problem and free memory -2. **Binding the NIC to the DPDK Driver** +3. **Binding the NIC to the DPDK Driver** You can check the current status of NIC port bindings with - `sudo ./tools/dpdk_nic_bind.py --status` + `sudo ./usertools/dpdk-devbind.py --status` Output similar to below will show what driver each NIC port is bound to. @@ -221,11 +245,11 @@ Troubleshooting `sudo ifconfig eth2 down` - Rerun the status command, `./tools/dpdk_nic_bind.py --status`, to see that it is not active anymore. Once that is done, proceed to bind the NIC port to the DPDK Kenrel module: + Rerun the status command, `./usertools/dpdk-devbind.py --status`, to see that it is not active anymore. Once that is done, proceed to bind the NIC port to the DPDK Kenrel module: - `sudo ./tools/dpdk_nic_bind.py -b igb_uio 07:00.0` + `sudo ./usertools/dpdk-devbind.py -b igb_uio 07:00.0` - Check the status again, `./tools/dpdk_nic_bind.py --status`, and assure the output is similar to our example below: + Check the status again, `./usertools/dpdk-devbind.py --status`, and assure the output is similar to our example below: ``` Network devices using DPDK-compatible driver @@ -238,13 +262,13 @@ Troubleshooting 0000:05:00.1 '82576 Gigabit Network Connection' if=eth1 drv=igb unused=igb_uio 0000:07:00.1 '82599EB 10-Gigabit SFI/SFP+ Network Connection' if=eth3 drv=ixgbe unused=igb_uio ``` -3. **Exporting $ONVM_HOME** +4. **Exporting $ONVM_HOME** If the setup_environment.sh script fails because the environment variable ONVM_HOME is not set, please run this command: `export ONVM_HOME=$ONVM_HOME:CHANGEME_TO_THE_PATH_TO_ONVM_DIR` -4. **Poor Performance** +5. **Poor Performance** -If you are not getting the expected level of performance, try these: + If you are not getting the expected level of performance, try these: - - Ensure the manager and NFs are all given different core numbers. Use cores on the same sockets for best results. - - If running a long chain of NFs, ensure that there are sufficient packets to keep the chain busy. If using locally generated packets (i.e., the Speed Tester NFs) then use the `-c` flag to increase the number of packets created. For best results, run multiple Speed Tester NFs, or use an external generator like pktgen. + - Ensure the manager and NFs are all given different core numbers. Use cores on the same sockets for best results. + - If running a long chain of NFs, ensure that there are sufficient packets to keep the chain busy. If using locally generated packets (i.e., the Speed Tester NFs) then use the `-c` flag to increase the number of packets created. For best results, run multiple Speed Tester NFs, or use an external generator like pktgen. diff --git a/docs/NF_Dev.md b/docs/NF_Dev.md index 247184d76..389080997 100644 --- a/docs/NF_Dev.md +++ b/docs/NF_Dev.md @@ -13,8 +13,9 @@ NFs are run with different arguments in three different tiers--DPDK configuratio + Flags to configure how DPDK is initialized and run. NFs typically use these arguments: - `-l CPU_CORE_LIST -n 3 --proc-type=secondary` - openNetVM configuration flags: - + Flags to configure how the NF is managed by openNetVM. NFs can configure their service ID and, for debugging, their instance ID (the manager automatically assigns instance IDs, but sometimes it is useful to manually assign them). NFs can also select to share cores with other NFs and enable manual core selection that overrides the onvm_mgr core selection (if core is available): - - `-r SERVICE_ID [-n INSTANCE_ID] [-s SHARE_CORE] [-m MANUAL_CORE_SELECTION]` + + Flags to configure how the NF is managed by openNetVM. NFs can configure their service ID and, for debugging, their instance ID (the manager automatically assigns instance IDs, but sometimes it is useful to manually assign them). NFs can also select to share cores with other NFs and enable manual core selection that overrides the onvm_mgr core selection (if core is available), their time to live and their packet limit (which is a packet based ttl): + + - `-r SERVICE_ID [-n INSTANCE_ID] [-s SHARE_CORE] [-m MANUAL_CORE_SELECTION] [-t TIME_TO_LIVE] [-l PACKET_LIMIT]` - NF configuration flags: + User defined flags to configure NF parameters. Some of our example NFs use a flag to throttle how often packet info is printed, or to specify a destination NF to send packets to. See the [simple_forward][forward] NF for an example of them both. @@ -41,6 +42,20 @@ Example use of Advanced Rings can be seen in the speed_tester NF or the scaling NFs can scale by running multiple threads. For launching more threads the main NF had to be launched with more than 1 core. For running a new thread the NF should call `onvm_nflib_scale(struct onvm_nf_scale_info *scale_info)`. The `struct scale_info` has all the required information for starting a new child NF, service and instance ids, NF state data, and the packet handling functions. The struct can be obtained either by calling the `onvm_nflib_get_empty_scaling_config(struct onvm_nf_info *parent_info)` and manually filling it in or by inheriting the parent behavior by using `onvm_nflib_inherit_parent_config(struct onvm_nf_info *parent_info)`. As the spawned NFs are threads they will share all the global variables with its parent, the `onvm_nf_info->data` is a void pointer that should be used for NF state data. Example use of Multithreading NF scaling functionality can be seen in the scaling_example NF. +### Shared core mode +This is an **EXPERIMENTAL** mode for OpenNetVM. It allows multiple NFs to run on a shared core. In "normal" OpenNetVM, each NF will poll its RX queue for packets, monopolizing the CPU even if it has a low load. This branch adds a semaphore-based communication system so that NFs will block when there are no packets available. The NF Manger will then signal the semaphore once one or more packets arrive. + +This code allows you to evaluate resource management techniques for NFs that share cores, however it has not been fully tested with complex NFs, therefore if you encounter any bugs please create an issue or a pull request with a proposed fix. + +The code is based on the hybrid-polling model proposed in [_Flurries: Countless Fine-Grained NFs for Flexible Per-Flow Customization_ by Wei Zhang, Jinho Hwang, Shriram Rajagopalan, K. K. Ramakrishnan, and Timothy Wood, published at _Co-NEXT 16_][flurries_paper] and extended in [_NFVnice: Dynamic Backpressure and Scheduling for NFV Service Chains_ by Sameer G. Kulkarni, Wei Zhang, Jinho Hwang, Shriram Rajagopalan, K. K. Ramakrishnan, Timothy Wood, Mayutan Arumaithurai and Xiaoming Fu, published at _SIGCOMM '17_][nfvnice_paper]. Note that this code does not contain the full Flurries or NFVnice systems, only the basic support for shared-Core NFs. However, we have recently released a full version of the NFVNice system as an experimental branch, which can be found [here][nfvnice_branch]. + +Usage / Known Limitations: + - To enable pass a `-c` flag to the onvm_mgr, and use a `-s` flag when starting a NF to specify that they want to share cores + - All code for sharing CPUs is within `if (ONVM_NF_SHARE_CORES)` blocks + - When enabled, you can run multiple NFs on the same CPU core with much less interference than if they are polling for packets + - This code does not provide any particular intelligence for how NFs are scheduled or when they wakeup/sleep + - Note that the manager threads all still use polling + Packet Helper Library -- @@ -102,4 +117,6 @@ values. [flow_director]: ../onvm/onvm_nflib/onvm_flow_dir.h [srvc_chains]: ../onvm/onvm_nflib/onvm_sc_common.h [msg_passing]: ../onvm/onvm_nflib/onvm_msg_common.h - +[flurries_paper]: https://dl.acm.org/citation.cfm?id=2999602 +[nfvnice_paper]: https://dl.acm.org/citation.cfm?id=3098828 +[nfvnice_branch]: https://github.com/sdnfv/openNetVM/tree/experimental/nfvnice-reinforce diff --git a/docs/Releases.md b/docs/Releases.md index 8d2ea15fd..e4c9259aa 100644 --- a/docs/Releases.md +++ b/docs/Releases.md @@ -13,6 +13,329 @@ use a date based versioning system. Now, a release version can look like `17.11` where the "major" number is the year and the "minor" number is the month. +## v19.05 (5/19): Shared Core Mode, Major Architectural Changes, Advanced Rings Changes, Stats Updates, CI PR Review, LPM Firewall NF, Payload Search NF, TTL Flags, minor improvements and bug fixes. +A CloudLab template is available with the latest release here: https://www.cloudlab.us/p/GWCloudLab/onvm + +**This release features a lot of breaking API changes.** + +**Performance**: This release increases Pktgen benchmark performance from 7Mpps to 13.1 Mpps (measured by Pktgen sending packets to the ONVM Basic Monitor), thus fixing the major performance issue that was present in the last release. + +**Repo changes**: Default branch has been changed to `master`, active development can still be seen in `develop`. Most of the development is now done on the public repo to improve visibility, planned projects and improvements can be seen in this [pinned issue](https://github.com/sdnfv/openNetVM/issues/91), additionally pull requests and issues are now cataloged by tags. We're also starting to merge releases into master by pull requests, thus developers should branch off the develop branch and submit PRs against the develop branch. + +**Note**: If the NFs crash with this error - `Cannot mmap memory for rte_config at [0x7ffff7ff3000], got [0x7ffff7ff2000]`, simply use the `-a 0x7f000000000` flag for the onvm_mgr, this will resolve the issue. + +### Shared Core Mode: +This code introduces **EXPERIMENTAL** support to allow NFs to efficiently run on **shared** CPU cores. NFs wait on semaphores when idle and are signaled by the manager when new packets arrive. Once the NF is in wake state, no additional notifications will be sent until it goes back to sleep. Shared core variables for mgr are in the `nf_wakeup_info` structs, the NF shared core vars were moved to the `onvm_nf` struct. + +The code is based on the hybrid-polling model proposed in [_Flurries: Countless Fine-Grained NFs for Flexible Per-Flow Customization_ by Wei Zhang, Jinho Hwang, Shriram Rajagopalan, K. K. Ramakrishnan, and Timothy Wood, published at _Co-NEXT 16_][flurries_paper] and extended in [_NFVnice: Dynamic Backpressure and Scheduling for NFV Service Chains_ by Sameer G. Kulkarni, Wei Zhang, Jinho Hwang, Shriram Rajagopalan, K. K. Ramakrishnan, Timothy Wood, Mayutan Arumaithurai and Xiaoming Fu, published at _SIGCOMM '17_][nfvnice_paper]. Note that this code does not contain the full Flurries or NFVnice systems, only the basic support for shared-Core NFs. However, we have recently released a full version of the NFVNice system as an experimental branch, which can be found [here][nfvnice_branch]. + +Usage and implementation details can be found [here][shared_core_docs]. + +### Major Architectural Changes: +- Introduce a local `onvm_nf_init_ctx` struct allocated from the heap before starting onvm. + + Previously the initialization sequence for NFs wasn't able to properly cleanup if a signal was received. Because of this we have introduced a new NF context struct(`onvm_nf_local_ctx`) which would be malloced before initialization begins and would help handle cleanup. This struct contains relevant information about the status of the initialization sequence and holds a reference to the `onvm_nf` struct which has all the information about the NF. + +- Reworking the `onvm_nf` struct. + + Previously the `onvm_nf` struct contained a pointer to the `onvm_nf_info`, which was used during processing. It's better to have one main struct that represents the NF, thus the contents of the `onvm_nf_info` were merged into the `onvm_nf` struct. This allows us to maintain a cleaner API where all information about the NF is stored in the `onvm_nf` struct. + + - Replace the old `onvm_nf_info` with a new `onvm_nf_init_ctx` struct that is passed to onvm_mgr for initialization. + + This struct contains all relevant information to spawn a new NF (service/instance IDs, flags, core, etc). When the NF is spawned this struct will be released back to the mempool. + + + - Adding a function table struct `onvm_nf_function_table`. + + Finally, we introduced the `onvm_nf_function_table` struct that groups all NF callback functions that can be set by developers. + +**Overall, the new NF launch/shutdown sequence looks as follows:** +```c +struct onvm_nf_local_ctx *nf_local_ctx; +struct onvm_nf_function_table *nf_function_table; + +nf_local_ctx = onvm_nflib_init_nf_local_ctx(); +onvm_nflib_start_signal_handler(nf_local_ctx, NULL); + +nf_function_table = onvm_nflib_init_nf_function_table(); +nf_function_table->pkt_handler = &packet_handler; + +if ((arg_offset = onvm_nflib_init(argc, argv, NF_TAG, nf_local_ctx, nf_function_table)) < 0) + // error checks + +argc -= arg_offset; +argv += arg_offset; + +if (parse_app_args(argc, argv, progname) < 0) + // error checks + +onvm_nflib_run(nf_local_ctx); +onvm_nflib_stop(nf_local_ctx); +``` + +### Advanced Rings Changes: +This release changes our approach to NFs using the advanced rings mode. Previously we were trying to provide APIs for advanced ring developers such as scaling, but this logic should be managed by the NFs themselves. Because of this we're reworking those APIs and letting the NF devs handle everything themselves. + - Speed Tester NF advanced rings mode is removed + - Extra APIs have been removed + - Removes support for advanced rings scaling APIs + - Scaling Example NF advanced rings mode has been reworked, the new implementation now does its own pthread creation instead of relying on the onvm scaling APIs. Also makes a clear separation between default and advanced ring mode. + - Because of these changes some internal nflib APIs were exposed to the NF (`onvm_nflib_start_nf`, `onvm_nflib_init_nf_init_cfg`, `onvm_nflib_inherit_parent_init_cfg`) + +### Stats Updates: +This release updates both console and web stats. + + - For web stats this adds the Core Mappings page with the core layout for both onvm_mgr and NFs. + - For console stats this overhauls the displayed stats and adds new information, see more below. + +The new default mode now displays NF tag and core ID: +``` +PORTS +----- +Port 0: '90:e2:ba:b3:bc:6c' + +Port 0 - rx: 4 ( 0 pps) tx: 0 ( 0 pps) + +NF TAG IID / SID / CORE rx_pps / tx_pps rx_drop / tx_drop out / tonf / drop +---------------------------------------------------------------------------------------------------------------------- +speed_tester 1 / 1 / 4 1693920 / 1693920 0 / 0 0 / 40346970 / 0 +``` + +Verbose mode also adds `PNT`(Parent ID), `S|W`(NF state, sleeping or working), `CHLD`(Children count): +``` +PORTS +----- +Port 0: '90:e2:ba:b3:bc:6c' + +Port 0 - rx: 4 ( 0 pps) tx: 0 ( 0 pps) + +NF TAG IID / SID / CORE rx_pps / tx_pps rx_drop / tx_drop out / tonf / drop + PNT / S|W / CHLD drop_pps / drop_pps rx_drop / tx_drop next / buf / ret +---------------------------------------------------------------------------------------------------------------------- +speed_tester 1 / 1 / 4 9661664 / 9661664 94494528 / 94494528 0 / 94494487 / 0 + 0 / W / 0 0 / 0 0 / 0 0 / 0 / 128 +``` + +The shared core mode adds wakeup information stats: +``` +PORTS +----- +Port 0: '90:e2:ba:b3:bc:6c' + +Port 0 - rx: 5 ( 0 pps) tx: 0 ( 0 pps) + +NF TAG IID / SID / CORE rx_pps / tx_pps rx_drop / tx_drop out / tonf / drop + PNT / S|W / CHLD drop_pps / drop_pps rx_drop / tx_drop next / buf / ret + wakeups / wakeup_rt +---------------------------------------------------------------------------------------------------------------------- +simple_forward 2 / 2 / 4 27719 / 27719 764439 / 764439 0 / 764439 / 0 + 0 / S / 0 0 / 0 0 / 0 0 / 0 / 0 + 730557 / 25344 + +speed_tester 3 / 1 / 5 27719 / 27719 764440 / 764439 0 / 764440 / 0 + 0 / W / 0 0 / 0 0 / 0 0 / 0 / 1 + 730560 / 25347 + + + +Shared core stats +----------------- +Total wakeups = 1461122, Wakeup rate = 50696 +``` + +The super verbose stats mode has also been updated to include new stats: +``` +#YYYY-MM-DD HH:MM:SS,nic_rx_pkts,nic_rx_pps,nic_tx_pkts,nic_tx_pps +#YYYY-MM-DD HH:MM:SS,nf_tag,instance_id,service_id,core,parent,state,children_cnt,rx,tx,rx_pps,tx_pps,rx_drop,tx_drop,rx_drop_rate,tx_drop_rate,act_out,act_tonf,act_drop,act_next,act_buffer,act_returned,num_wakeups,wakeup_rate +2019-06-04 08:54:52,0,4,4,0,0 +2019-06-04 08:54:53,0,4,0,0,0 +2019-06-04 08:54:54,simple_forward,1,2,4,0,W,0,29058,29058,29058,29058,0,0,0,0,0,29058,0,0,0,0,28951,28951 +2019-06-04 08:54:54,speed_tester,2,1,5,0,S,0,29058,29058,29058,29058,0,0,0,0,0,29059,0,0,0,1,28952,28952 +2019-06-04 08:54:55,0,4,0,0,0 +2019-06-04 08:54:55,simple_forward,1,2,4,0,W,0,101844,101843,72785,72785,0,0,0,0,0,101843,0,0,0,0,101660,101660 +2019-06-04 08:54:55,speed_tester,2,1,5,0,W,0,101844,101843,72785,72785,0,0,0,0,0,101844,0,0,0,1,101660,101660 +``` + +### CI PR Review: +CI is now available on the public branch. Only a specific list of whitelisted users can currently run CI for security purposes. The new CI system is able to approve/reject pull requests. +CI currently performs these checks: + - Check the branch (for our discussed change of develop->master as main branch) + - Run performance check (speed tester currently with 35mil benchmark) + - Run linter (only on the PR diff) + +### LPM Firewall NF: +The firewall NF drops or forwards packets based on rules provided in a JSON config file. This is achieved using DPDK's LPM (longest prefix matching) library. Default behavior is to drop a packet unless the packet matches a rule. The NF also has a debug mode to print decisions for every packet and an inverse match mode where default behavior is to forward a packet if it is not found in the table. Documentation for this NF can be found [here][firewall_nf_readme]. + +### Payload Search NF: +The Payload Scan NF provides the functionality to search for a string within a given UDP or TCP packet payload. Packet is forwarded to its destination NF on a match, dropped otherwise. The NF also has an inverse mode to drop on match and forward otherwise. Documentation for this NF can be found [here][payload_scan_nf_readme]. + +### TTL Flags: +Adds TTL and packet limit flags to stop the NF or the onvm_mgr based on time since startup or based on packets received. Default measurements for these flags are in seconds and in millions of packets received. + +### NF to NF Messaging: +Adds the ability for NFs to send messages to other NFs. NFs need to define a message handler to receive messages and are responsible to free the custom message data. If the message is sent to a NF that doesn't have a message handler the message is ignored. + +### Minor improvements + - **Make Number of mbufs a Constant Value** - Previously the number of mbufs was calculated based on the `MAX_NFS` constant. This led to performance degradation as the requested number of mbufs was too high, changing this to a constant has significantly improved performance. + - **Reuse NF Instance IDs** - Reuse instance IDs of old NFs that have terminated. The instance IDs are still continiously incremented up to the `MAX_NFS` constant, but when that number is reached the next NF instance ID will be wrapped back to the starting value and find the first unoccupied instance ID. + - Fix all major style errors + - Check if ONVM_HOME is Set Before Compiling ONVM + - Add Core Information to Web Stats + - Update Install Script Hugepage Setup & Kernel Driver Installation + - Add Compatibility Changes to Run ONVM on Ubuntu 18.04.1 + - Various Documentation updates and fixes + - Change onvm-pktgen Submodule to Upstream Pktgen + +### Bug fixes: + - Free Memory on ONVM_MGR Shutdown + - Launch Script to Handle Multi-word String Arguments + - NF Advanced Ring Thread Process NF Shutdown Messages + - Adds NF Ring Cleanup Logic On Shutdown + - Resolve Shutdown Memory Leaks + - Add NF Tag Memory Allocation + - Fix the Parse IP Helper Function + - Fix Speed Tester NF Generated Packets Counter + - Add Termination of Started but not yet Running NFs + - Add ONVM mgr web mode memory cleanup on shutdown + - Removes the Old Flow Tracker NF Launch Script + - Fix Deprecated DPDK Function in Speed Tester NF + +**v19.05 API Struct changes:** + +* Adding `onvm_nf_local_ctx` which is malloced and passed into `onvm_nflib_init`: + ```c + struct onvm_nf_local_ctx { + struct onvm_nf *nf; + rte_atomic16_t nf_init_finished; + rte_atomic16_t keep_running; + }; + ``` + +* Adding a function table for eaiser callback managing: + ```c + struct onvm_nf_function_table { + nf_setup_fn setup; + nf_msg_handler_fn msg_handler; + nf_user_actions_fn user_actions; + nf_pkt_handler_fn pkt_handler; + }; + ``` + +* Renaming the old `onvm_nf_info` -> `onvm_nf_init_cfg`: + ```c + struct onvm_nf_init_cfg { + uint16_t instance_id; + uint16_t service_id; + uint16_t core; + uint16_t init_options; + uint8_t status; + char *tag; + /* If set NF will stop after time reaches time_to_live */ + uint16_t time_to_live; + /* If set NF will stop after pkts TX reach pkt_limit */ + uint16_t pkt_limit; + }; + ``` + +* Consolidating previous `onvm_nf_info` and `onvm_nf` into a singular `onvm_nf` struct: + ```c + struct onvm_nf { + struct rte_ring *rx_q; + struct rte_ring *tx_q; + struct rte_ring *msg_q; + /* Struct for NF to NF communication (NF tx) */ + struct queue_mgr *nf_tx_mgr; + uint16_t instance_id; + uint16_t service_id; + uint8_t status; + char *tag; + /* Pointer to NF defined state data */ + void *data; + + struct { + uint16_t core; + /* Instance ID of parent NF or 0 */ + uint16_t parent; + rte_atomic16_t children_cnt; + } thread_info; + + struct { + uint16_t init_options; + /* If set NF will stop after time reaches time_to_live */ + uint16_t time_to_live; + /* If set NF will stop after pkts TX reach pkt_limit */ + uint16_t pkt_limit; + } flags; + + /* NF specific functions */ + struct onvm_nf_function_table *function_table; + + /* + * Define a structure with stats from the NFs. + * + * These stats hold how many packets the NF will actually receive, send, + * and how many packets were dropped because the NF's queue was full. + * The port-info stats, in contrast, record how many packets were received + * or transmitted on an actual NIC port. + */ + struct { + volatile uint64_t rx; + volatile uint64_t rx_drop; + volatile uint64_t tx; + volatile uint64_t tx_drop; + volatile uint64_t tx_buffer; + volatile uint64_t tx_returned; + volatile uint64_t act_out; + volatile uint64_t act_tonf; + volatile uint64_t act_drop; + volatile uint64_t act_next; + volatile uint64_t act_buffer; + } stats; + + struct { + /* + * Sleep state (shared mem variable) to track state of NF and trigger wakeups + * sleep_state = 1 => NF sleeping (waiting on semaphore) + * sleep_state = 0 => NF running (not waiting on semaphore) + */ + rte_atomic16_t *sleep_state; + /* Mutex for NF sem_wait */ + sem_t *nf_mutex; + } shared_core; + }; + ``` + +**v19.05 API Changes:** + - `int onvm_nflib_init(int argc, char *argv[], const char *nf_tag, struct onvm_nf_info **nf_info_p)` -> `int onvm_nflib_init(int argc, char *argv[], const char *nf_tag, struct onvm_nf_local_ctx *nf_local_ctx, struct onvm_nf_function_table *nf_function_table)` + - `int onvm_nflib_run(struct onvm_nf_info* info, pkt_handler_func pkt_handler)` -> `int onvm_nflib_run(struct onvm_nf_local_ctx *nf_local_ctx)` + - `int onvm_nflib_return_pkt(struct onvm_nf_info *nf_info, struct rte_mbuf* pkt)` -> `int onvm_nflib_return_pkt(struct onvm_nf *nf, struct rte_mbuf *pkt)` + - `int onvm_nflib_return_pkt_bulk(struct onvm_nf_info *nf_info, struct rte_mbuf** pkts, uint16_t count)` -> `onvm_nflib_return_pkt_bulk(struct onvm_nf *nf, struct rte_mbuf **pkts, uint16_t count)` + - `int onvm_nflib_nf_ready(struct onvm_nf_info *info)` -> `int onvm_nflib_nf_ready(struct onvm_nf *nf)` + - `int onvm_nflib_handle_msg(struct onvm_nf_msg *msg, __attribute__((unused)) struct onvm_nf_info *nf_info)` -> + `int onvm_nflib_handle_msg(struct onvm_nf_msg *msg, struct onvm_nf_local_ctx *nf_local_ctx)` + - `void onvm_nflib_stop(struct onvm_nf_info *nf_info)` -> `void onvm_nflib_stop(struct onvm_nf_local_ctx *nf_local_ctx)` + - `struct onvm_nf_scale_info *onvm_nflib_get_empty_scaling_config(struct onvm_nf_info *parent_info)` -> `struct onvm_nf_scale_info *onvm_nflib_get_empty_scaling_config(struct onvm_nf *nf)` + - `struct onvm_nf_scale_info *onvm_nflib_inherit_parent_config(struct onvm_nf_info *parent_info, void *data)` -> +`struct onvm_nf_scale_info *onvm_nflib_inherit_parent_config(struct onvm_nf *nf, void *data)` + +**v19.05 API Additions:** + - `struct onvm_nf_local_ctx *onvm_nflib_init_nf_local_ctx(void)` + - `struct onvm_nf_function_table *onvm_nflib_init_nf_function_table(void)` + - `int onvm_nflib_start_signal_handler(struct onvm_nf_local_ctx *nf_local_ctx, handle_signal_func signal_hanlder)` + - `int onvm_nflib_send_msg_to_nf(uint16_t dest_nf, void *msg_data)` + - `int onvm_nflib_request_lpm(struct lpm_request *req)` + - `struct onvm_configuration *onvm_nflib_get_onvm_config(void)` + These APIs were previously internal but are now exposed for advanced ring NFs: + - `int onvm_nflib_start_nf(struct onvm_nf_local_ctx *nf_local_ctx, struct onvm_nf_init_cfg *nf_init_cfg)` + - `struct onvm_nf_init_cfg *onvm_nflib_init_nf_init_cfg(const char *tag)` + - `struct onvm_nf_init_cfg *onvm_nflib_inherit_parent_init_cfg(struct onvm_nf *parent)` + +**v19.05 Removed APIs:** + - `int onvm_nflib_run_callback(struct onvm_nf_info* info, pkt_handler_func pkt_handler, callback_handler_func callback_handler)` + - `struct rte_ring *onvm_nflib_get_tx_ring(struct onvm_nf_info* info)` + - `struct rte_ring *onvm_nflib_get_rx_ring(struct onvm_nf_info* info)` + - `struct onvm_nf *onvm_nflib_get_nf(uint16_t id)` + - `void onvm_nflib_set_setup_function(struct onvm_nf_info* info, setup_func setup)` + ## v19.02 (2/19): Manager Assigned NF Cores, Global Launch Script, DPDK 18.11 Update, Web Stats Overhaul, Load Generator NF, CI (Internal repo only), minor improvements and bug fixes This release adds several new features and changes how the onvm_mgr and NFs start. A CloudLab template is available with the latest release here: https://www.cloudlab.us/p/GWCloudLab/onvm @@ -290,3 +613,12 @@ Each module comes with a header file with commented prototypes. And each c and h ## 4/24/16: Initial Release Initial source code release. + + + +[firewall_nf_readme]: ../examples/firewall/README.md +[payload_scan_nf_readme]: ../examples/payload_scan/README.md +[shared_core_docs]: ./NF_Dev.md#shared-cpu-mode +[nfvnice_branch]: https://github.com/sdnfv/openNetVM/tree/experimental/nfvnice-reinforce +[flurries_paper]: https://dl.acm.org/citation.cfm?id=2999602 +[nfvnice_paper]: https://dl.acm.org/citation.cfm?id=3098828 diff --git a/examples/Makefile b/examples/Makefile index 4a1a82961..b3f0adb62 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -40,7 +40,7 @@ $(error "Please define RTE_SDK environment variable") endif # To add new examples, append the directory name to this variable -examples = bridge basic_monitor simple_forward speed_tester flow_table test_flow_dir aes_encrypt aes_decrypt flow_tracker load_balancer arp_response nf_router scaling_example load_generator +examples = bridge basic_monitor simple_forward speed_tester flow_table test_flow_dir aes_encrypt aes_decrypt flow_tracker load_balancer arp_response nf_router scaling_example load_generator payload_scan firewall ifeq ($(NDPI_HOME),) $(warning "Skipping ndpi_stats NF as NDPI_HOME is not set") diff --git a/examples/aes_decrypt/aes.c b/examples/aes_decrypt/aes.c index 61848c10f..4b9c99ac1 100644 --- a/examples/aes_decrypt/aes.c +++ b/examples/aes_decrypt/aes.c @@ -5,9 +5,9 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside - * 2016-2017 Hewlett Packard Enterprise Development LP + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside + * 2016-2019 Hewlett Packard Enterprise Development LP * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -54,82 +54,94 @@ * http://csrc.nist.gov/publications/nistpubs/800-38C/SP800-38C_updated-July20_2007.pdf * Source: https://github.com/B-Con/crypto-algorithms *********************************************************************/ - + /*************************** HEADER FILES ***************************/ -#include -#include #include "aes.h" - +#include +#include + #include - + /****************************** MACROS ******************************/ // The least significant byte of the word is rotated to the end. #define KE_ROTWORD(x) (((x) << 8) | ((x) >> 24)) - -#define TRUE 1 + +#define TRUE 1 #define FALSE 0 - + /**************************** DATA TYPES ****************************/ #define AES_128_ROUNDS 10 #define AES_192_ROUNDS 12 #define AES_256_ROUNDS 14 - + /*********************** FUNCTION DECLARATIONS **********************/ -void ccm_prepare_first_ctr_blk(BYTE counter[], const BYTE nonce[], int nonce_len, int payload_len_store_size); -void ccm_prepare_first_format_blk(BYTE buf[], int assoc_len, int payload_len, int payload_len_store_size, int mac_len, const BYTE nonce[], int nonce_len); -void ccm_format_assoc_data(BYTE buf[], int *end_of_buf, const BYTE assoc[], int assoc_len); -void ccm_format_payload_data(BYTE buf[], int *end_of_buf, const BYTE payload[], int payload_len); -void xor_buf(const BYTE in[], BYTE out[], size_t len); -WORD SubWord(WORD word); -void AddRoundKey(BYTE state[][4], const WORD w[]); -void SubBytes(BYTE state[][4]); -void InvSubBytes(BYTE state[][4]); -void ShiftRows(BYTE state[][4]); -void MixColumns(BYTE state[][4]); -void InvMixColumns(BYTE state[][4]); -void InvShiftRows(BYTE state[][4]); - +void +ccm_prepare_first_ctr_blk(BYTE counter[], const BYTE nonce[], int nonce_len, int payload_len_store_size); +void +ccm_prepare_first_format_blk(BYTE buf[], int assoc_len, int payload_len, int payload_len_store_size, int mac_len, + const BYTE nonce[], int nonce_len); +void +ccm_format_assoc_data(BYTE buf[], int *end_of_buf, const BYTE assoc[], int assoc_len); +void +ccm_format_payload_data(BYTE buf[], int *end_of_buf, const BYTE payload[], int payload_len); +void +xor_buf(const BYTE in[], BYTE out[], size_t len); +WORD +SubWord(WORD word); +void +AddRoundKey(BYTE state[][4], const WORD w[]); +void +SubBytes(BYTE state[][4]); +void +InvSubBytes(BYTE state[][4]); +void +ShiftRows(BYTE state[][4]); +void +MixColumns(BYTE state[][4]); +void +InvMixColumns(BYTE state[][4]); +void +InvShiftRows(BYTE state[][4]); + /**************************** VARIABLES *****************************/ // This is the specified AES SBox. To look up a substitution value, put the first // nibble in the first index (row) and the second nibble in the second index (column). static const BYTE aes_sbox[16][16] = { - {0x63,0x7C,0x77,0x7B,0xF2,0x6B,0x6F,0xC5,0x30,0x01,0x67,0x2B,0xFE,0xD7,0xAB,0x76}, - {0xCA,0x82,0xC9,0x7D,0xFA,0x59,0x47,0xF0,0xAD,0xD4,0xA2,0xAF,0x9C,0xA4,0x72,0xC0}, - {0xB7,0xFD,0x93,0x26,0x36,0x3F,0xF7,0xCC,0x34,0xA5,0xE5,0xF1,0x71,0xD8,0x31,0x15}, - {0x04,0xC7,0x23,0xC3,0x18,0x96,0x05,0x9A,0x07,0x12,0x80,0xE2,0xEB,0x27,0xB2,0x75}, - {0x09,0x83,0x2C,0x1A,0x1B,0x6E,0x5A,0xA0,0x52,0x3B,0xD6,0xB3,0x29,0xE3,0x2F,0x84}, - {0x53,0xD1,0x00,0xED,0x20,0xFC,0xB1,0x5B,0x6A,0xCB,0xBE,0x39,0x4A,0x4C,0x58,0xCF}, - {0xD0,0xEF,0xAA,0xFB,0x43,0x4D,0x33,0x85,0x45,0xF9,0x02,0x7F,0x50,0x3C,0x9F,0xA8}, - {0x51,0xA3,0x40,0x8F,0x92,0x9D,0x38,0xF5,0xBC,0xB6,0xDA,0x21,0x10,0xFF,0xF3,0xD2}, - {0xCD,0x0C,0x13,0xEC,0x5F,0x97,0x44,0x17,0xC4,0xA7,0x7E,0x3D,0x64,0x5D,0x19,0x73}, - {0x60,0x81,0x4F,0xDC,0x22,0x2A,0x90,0x88,0x46,0xEE,0xB8,0x14,0xDE,0x5E,0x0B,0xDB}, - {0xE0,0x32,0x3A,0x0A,0x49,0x06,0x24,0x5C,0xC2,0xD3,0xAC,0x62,0x91,0x95,0xE4,0x79}, - {0xE7,0xC8,0x37,0x6D,0x8D,0xD5,0x4E,0xA9,0x6C,0x56,0xF4,0xEA,0x65,0x7A,0xAE,0x08}, - {0xBA,0x78,0x25,0x2E,0x1C,0xA6,0xB4,0xC6,0xE8,0xDD,0x74,0x1F,0x4B,0xBD,0x8B,0x8A}, - {0x70,0x3E,0xB5,0x66,0x48,0x03,0xF6,0x0E,0x61,0x35,0x57,0xB9,0x86,0xC1,0x1D,0x9E}, - {0xE1,0xF8,0x98,0x11,0x69,0xD9,0x8E,0x94,0x9B,0x1E,0x87,0xE9,0xCE,0x55,0x28,0xDF}, - {0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68,0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16} -}; - + {0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76}, + {0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0}, + {0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15}, + {0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75}, + {0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84}, + {0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF}, + {0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8}, + {0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2}, + {0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73}, + {0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB}, + {0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79}, + {0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08}, + {0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A}, + {0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E}, + {0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF}, + {0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16}}; + static const BYTE aes_invsbox[16][16] = { - {0x52,0x09,0x6A,0xD5,0x30,0x36,0xA5,0x38,0xBF,0x40,0xA3,0x9E,0x81,0xF3,0xD7,0xFB}, - {0x7C,0xE3,0x39,0x82,0x9B,0x2F,0xFF,0x87,0x34,0x8E,0x43,0x44,0xC4,0xDE,0xE9,0xCB}, - {0x54,0x7B,0x94,0x32,0xA6,0xC2,0x23,0x3D,0xEE,0x4C,0x95,0x0B,0x42,0xFA,0xC3,0x4E}, - {0x08,0x2E,0xA1,0x66,0x28,0xD9,0x24,0xB2,0x76,0x5B,0xA2,0x49,0x6D,0x8B,0xD1,0x25}, - {0x72,0xF8,0xF6,0x64,0x86,0x68,0x98,0x16,0xD4,0xA4,0x5C,0xCC,0x5D,0x65,0xB6,0x92}, - {0x6C,0x70,0x48,0x50,0xFD,0xED,0xB9,0xDA,0x5E,0x15,0x46,0x57,0xA7,0x8D,0x9D,0x84}, - {0x90,0xD8,0xAB,0x00,0x8C,0xBC,0xD3,0x0A,0xF7,0xE4,0x58,0x05,0xB8,0xB3,0x45,0x06}, - {0xD0,0x2C,0x1E,0x8F,0xCA,0x3F,0x0F,0x02,0xC1,0xAF,0xBD,0x03,0x01,0x13,0x8A,0x6B}, - {0x3A,0x91,0x11,0x41,0x4F,0x67,0xDC,0xEA,0x97,0xF2,0xCF,0xCE,0xF0,0xB4,0xE6,0x73}, - {0x96,0xAC,0x74,0x22,0xE7,0xAD,0x35,0x85,0xE2,0xF9,0x37,0xE8,0x1C,0x75,0xDF,0x6E}, - {0x47,0xF1,0x1A,0x71,0x1D,0x29,0xC5,0x89,0x6F,0xB7,0x62,0x0E,0xAA,0x18,0xBE,0x1B}, - {0xFC,0x56,0x3E,0x4B,0xC6,0xD2,0x79,0x20,0x9A,0xDB,0xC0,0xFE,0x78,0xCD,0x5A,0xF4}, - {0x1F,0xDD,0xA8,0x33,0x88,0x07,0xC7,0x31,0xB1,0x12,0x10,0x59,0x27,0x80,0xEC,0x5F}, - {0x60,0x51,0x7F,0xA9,0x19,0xB5,0x4A,0x0D,0x2D,0xE5,0x7A,0x9F,0x93,0xC9,0x9C,0xEF}, - {0xA0,0xE0,0x3B,0x4D,0xAE,0x2A,0xF5,0xB0,0xC8,0xEB,0xBB,0x3C,0x83,0x53,0x99,0x61}, - {0x17,0x2B,0x04,0x7E,0xBA,0x77,0xD6,0x26,0xE1,0x69,0x14,0x63,0x55,0x21,0x0C,0x7D} -}; - + {0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB}, + {0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB}, + {0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E}, + {0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25}, + {0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92}, + {0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84}, + {0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06}, + {0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B}, + {0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73}, + {0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E}, + {0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B}, + {0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4}, + {0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F}, + {0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF}, + {0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61}, + {0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D}}; + // This table stores pre-calculated values for all possible GF(2^8) calculations.This // table is only used by the (Inv)MixColumns steps. // USAGE: The second index (column) is the coefficient of multiplication. Only 7 different @@ -137,986 +149,1041 @@ static const BYTE aes_invsbox[16][16] = { // 1 is negligible leaving only 6 coefficients. Each column of the table is devoted to one // of these coefficients, in the ascending order of value, from values 0x00 to 0xFF. static const BYTE gf_mul[256][6] = { - {0x00,0x00,0x00,0x00,0x00,0x00},{0x02,0x03,0x09,0x0b,0x0d,0x0e}, - {0x04,0x06,0x12,0x16,0x1a,0x1c},{0x06,0x05,0x1b,0x1d,0x17,0x12}, - {0x08,0x0c,0x24,0x2c,0x34,0x38},{0x0a,0x0f,0x2d,0x27,0x39,0x36}, - {0x0c,0x0a,0x36,0x3a,0x2e,0x24},{0x0e,0x09,0x3f,0x31,0x23,0x2a}, - {0x10,0x18,0x48,0x58,0x68,0x70},{0x12,0x1b,0x41,0x53,0x65,0x7e}, - {0x14,0x1e,0x5a,0x4e,0x72,0x6c},{0x16,0x1d,0x53,0x45,0x7f,0x62}, - {0x18,0x14,0x6c,0x74,0x5c,0x48},{0x1a,0x17,0x65,0x7f,0x51,0x46}, - {0x1c,0x12,0x7e,0x62,0x46,0x54},{0x1e,0x11,0x77,0x69,0x4b,0x5a}, - {0x20,0x30,0x90,0xb0,0xd0,0xe0},{0x22,0x33,0x99,0xbb,0xdd,0xee}, - {0x24,0x36,0x82,0xa6,0xca,0xfc},{0x26,0x35,0x8b,0xad,0xc7,0xf2}, - {0x28,0x3c,0xb4,0x9c,0xe4,0xd8},{0x2a,0x3f,0xbd,0x97,0xe9,0xd6}, - {0x2c,0x3a,0xa6,0x8a,0xfe,0xc4},{0x2e,0x39,0xaf,0x81,0xf3,0xca}, - {0x30,0x28,0xd8,0xe8,0xb8,0x90},{0x32,0x2b,0xd1,0xe3,0xb5,0x9e}, - {0x34,0x2e,0xca,0xfe,0xa2,0x8c},{0x36,0x2d,0xc3,0xf5,0xaf,0x82}, - {0x38,0x24,0xfc,0xc4,0x8c,0xa8},{0x3a,0x27,0xf5,0xcf,0x81,0xa6}, - {0x3c,0x22,0xee,0xd2,0x96,0xb4},{0x3e,0x21,0xe7,0xd9,0x9b,0xba}, - {0x40,0x60,0x3b,0x7b,0xbb,0xdb},{0x42,0x63,0x32,0x70,0xb6,0xd5}, - {0x44,0x66,0x29,0x6d,0xa1,0xc7},{0x46,0x65,0x20,0x66,0xac,0xc9}, - {0x48,0x6c,0x1f,0x57,0x8f,0xe3},{0x4a,0x6f,0x16,0x5c,0x82,0xed}, - {0x4c,0x6a,0x0d,0x41,0x95,0xff},{0x4e,0x69,0x04,0x4a,0x98,0xf1}, - {0x50,0x78,0x73,0x23,0xd3,0xab},{0x52,0x7b,0x7a,0x28,0xde,0xa5}, - {0x54,0x7e,0x61,0x35,0xc9,0xb7},{0x56,0x7d,0x68,0x3e,0xc4,0xb9}, - {0x58,0x74,0x57,0x0f,0xe7,0x93},{0x5a,0x77,0x5e,0x04,0xea,0x9d}, - {0x5c,0x72,0x45,0x19,0xfd,0x8f},{0x5e,0x71,0x4c,0x12,0xf0,0x81}, - {0x60,0x50,0xab,0xcb,0x6b,0x3b},{0x62,0x53,0xa2,0xc0,0x66,0x35}, - {0x64,0x56,0xb9,0xdd,0x71,0x27},{0x66,0x55,0xb0,0xd6,0x7c,0x29}, - {0x68,0x5c,0x8f,0xe7,0x5f,0x03},{0x6a,0x5f,0x86,0xec,0x52,0x0d}, - {0x6c,0x5a,0x9d,0xf1,0x45,0x1f},{0x6e,0x59,0x94,0xfa,0x48,0x11}, - {0x70,0x48,0xe3,0x93,0x03,0x4b},{0x72,0x4b,0xea,0x98,0x0e,0x45}, - {0x74,0x4e,0xf1,0x85,0x19,0x57},{0x76,0x4d,0xf8,0x8e,0x14,0x59}, - {0x78,0x44,0xc7,0xbf,0x37,0x73},{0x7a,0x47,0xce,0xb4,0x3a,0x7d}, - {0x7c,0x42,0xd5,0xa9,0x2d,0x6f},{0x7e,0x41,0xdc,0xa2,0x20,0x61}, - {0x80,0xc0,0x76,0xf6,0x6d,0xad},{0x82,0xc3,0x7f,0xfd,0x60,0xa3}, - {0x84,0xc6,0x64,0xe0,0x77,0xb1},{0x86,0xc5,0x6d,0xeb,0x7a,0xbf}, - {0x88,0xcc,0x52,0xda,0x59,0x95},{0x8a,0xcf,0x5b,0xd1,0x54,0x9b}, - {0x8c,0xca,0x40,0xcc,0x43,0x89},{0x8e,0xc9,0x49,0xc7,0x4e,0x87}, - {0x90,0xd8,0x3e,0xae,0x05,0xdd},{0x92,0xdb,0x37,0xa5,0x08,0xd3}, - {0x94,0xde,0x2c,0xb8,0x1f,0xc1},{0x96,0xdd,0x25,0xb3,0x12,0xcf}, - {0x98,0xd4,0x1a,0x82,0x31,0xe5},{0x9a,0xd7,0x13,0x89,0x3c,0xeb}, - {0x9c,0xd2,0x08,0x94,0x2b,0xf9},{0x9e,0xd1,0x01,0x9f,0x26,0xf7}, - {0xa0,0xf0,0xe6,0x46,0xbd,0x4d},{0xa2,0xf3,0xef,0x4d,0xb0,0x43}, - {0xa4,0xf6,0xf4,0x50,0xa7,0x51},{0xa6,0xf5,0xfd,0x5b,0xaa,0x5f}, - {0xa8,0xfc,0xc2,0x6a,0x89,0x75},{0xaa,0xff,0xcb,0x61,0x84,0x7b}, - {0xac,0xfa,0xd0,0x7c,0x93,0x69},{0xae,0xf9,0xd9,0x77,0x9e,0x67}, - {0xb0,0xe8,0xae,0x1e,0xd5,0x3d},{0xb2,0xeb,0xa7,0x15,0xd8,0x33}, - {0xb4,0xee,0xbc,0x08,0xcf,0x21},{0xb6,0xed,0xb5,0x03,0xc2,0x2f}, - {0xb8,0xe4,0x8a,0x32,0xe1,0x05},{0xba,0xe7,0x83,0x39,0xec,0x0b}, - {0xbc,0xe2,0x98,0x24,0xfb,0x19},{0xbe,0xe1,0x91,0x2f,0xf6,0x17}, - {0xc0,0xa0,0x4d,0x8d,0xd6,0x76},{0xc2,0xa3,0x44,0x86,0xdb,0x78}, - {0xc4,0xa6,0x5f,0x9b,0xcc,0x6a},{0xc6,0xa5,0x56,0x90,0xc1,0x64}, - {0xc8,0xac,0x69,0xa1,0xe2,0x4e},{0xca,0xaf,0x60,0xaa,0xef,0x40}, - {0xcc,0xaa,0x7b,0xb7,0xf8,0x52},{0xce,0xa9,0x72,0xbc,0xf5,0x5c}, - {0xd0,0xb8,0x05,0xd5,0xbe,0x06},{0xd2,0xbb,0x0c,0xde,0xb3,0x08}, - {0xd4,0xbe,0x17,0xc3,0xa4,0x1a},{0xd6,0xbd,0x1e,0xc8,0xa9,0x14}, - {0xd8,0xb4,0x21,0xf9,0x8a,0x3e},{0xda,0xb7,0x28,0xf2,0x87,0x30}, - {0xdc,0xb2,0x33,0xef,0x90,0x22},{0xde,0xb1,0x3a,0xe4,0x9d,0x2c}, - {0xe0,0x90,0xdd,0x3d,0x06,0x96},{0xe2,0x93,0xd4,0x36,0x0b,0x98}, - {0xe4,0x96,0xcf,0x2b,0x1c,0x8a},{0xe6,0x95,0xc6,0x20,0x11,0x84}, - {0xe8,0x9c,0xf9,0x11,0x32,0xae},{0xea,0x9f,0xf0,0x1a,0x3f,0xa0}, - {0xec,0x9a,0xeb,0x07,0x28,0xb2},{0xee,0x99,0xe2,0x0c,0x25,0xbc}, - {0xf0,0x88,0x95,0x65,0x6e,0xe6},{0xf2,0x8b,0x9c,0x6e,0x63,0xe8}, - {0xf4,0x8e,0x87,0x73,0x74,0xfa},{0xf6,0x8d,0x8e,0x78,0x79,0xf4}, - {0xf8,0x84,0xb1,0x49,0x5a,0xde},{0xfa,0x87,0xb8,0x42,0x57,0xd0}, - {0xfc,0x82,0xa3,0x5f,0x40,0xc2},{0xfe,0x81,0xaa,0x54,0x4d,0xcc}, - {0x1b,0x9b,0xec,0xf7,0xda,0x41},{0x19,0x98,0xe5,0xfc,0xd7,0x4f}, - {0x1f,0x9d,0xfe,0xe1,0xc0,0x5d},{0x1d,0x9e,0xf7,0xea,0xcd,0x53}, - {0x13,0x97,0xc8,0xdb,0xee,0x79},{0x11,0x94,0xc1,0xd0,0xe3,0x77}, - {0x17,0x91,0xda,0xcd,0xf4,0x65},{0x15,0x92,0xd3,0xc6,0xf9,0x6b}, - {0x0b,0x83,0xa4,0xaf,0xb2,0x31},{0x09,0x80,0xad,0xa4,0xbf,0x3f}, - {0x0f,0x85,0xb6,0xb9,0xa8,0x2d},{0x0d,0x86,0xbf,0xb2,0xa5,0x23}, - {0x03,0x8f,0x80,0x83,0x86,0x09},{0x01,0x8c,0x89,0x88,0x8b,0x07}, - {0x07,0x89,0x92,0x95,0x9c,0x15},{0x05,0x8a,0x9b,0x9e,0x91,0x1b}, - {0x3b,0xab,0x7c,0x47,0x0a,0xa1},{0x39,0xa8,0x75,0x4c,0x07,0xaf}, - {0x3f,0xad,0x6e,0x51,0x10,0xbd},{0x3d,0xae,0x67,0x5a,0x1d,0xb3}, - {0x33,0xa7,0x58,0x6b,0x3e,0x99},{0x31,0xa4,0x51,0x60,0x33,0x97}, - {0x37,0xa1,0x4a,0x7d,0x24,0x85},{0x35,0xa2,0x43,0x76,0x29,0x8b}, - {0x2b,0xb3,0x34,0x1f,0x62,0xd1},{0x29,0xb0,0x3d,0x14,0x6f,0xdf}, - {0x2f,0xb5,0x26,0x09,0x78,0xcd},{0x2d,0xb6,0x2f,0x02,0x75,0xc3}, - {0x23,0xbf,0x10,0x33,0x56,0xe9},{0x21,0xbc,0x19,0x38,0x5b,0xe7}, - {0x27,0xb9,0x02,0x25,0x4c,0xf5},{0x25,0xba,0x0b,0x2e,0x41,0xfb}, - {0x5b,0xfb,0xd7,0x8c,0x61,0x9a},{0x59,0xf8,0xde,0x87,0x6c,0x94}, - {0x5f,0xfd,0xc5,0x9a,0x7b,0x86},{0x5d,0xfe,0xcc,0x91,0x76,0x88}, - {0x53,0xf7,0xf3,0xa0,0x55,0xa2},{0x51,0xf4,0xfa,0xab,0x58,0xac}, - {0x57,0xf1,0xe1,0xb6,0x4f,0xbe},{0x55,0xf2,0xe8,0xbd,0x42,0xb0}, - {0x4b,0xe3,0x9f,0xd4,0x09,0xea},{0x49,0xe0,0x96,0xdf,0x04,0xe4}, - {0x4f,0xe5,0x8d,0xc2,0x13,0xf6},{0x4d,0xe6,0x84,0xc9,0x1e,0xf8}, - {0x43,0xef,0xbb,0xf8,0x3d,0xd2},{0x41,0xec,0xb2,0xf3,0x30,0xdc}, - {0x47,0xe9,0xa9,0xee,0x27,0xce},{0x45,0xea,0xa0,0xe5,0x2a,0xc0}, - {0x7b,0xcb,0x47,0x3c,0xb1,0x7a},{0x79,0xc8,0x4e,0x37,0xbc,0x74}, - {0x7f,0xcd,0x55,0x2a,0xab,0x66},{0x7d,0xce,0x5c,0x21,0xa6,0x68}, - {0x73,0xc7,0x63,0x10,0x85,0x42},{0x71,0xc4,0x6a,0x1b,0x88,0x4c}, - {0x77,0xc1,0x71,0x06,0x9f,0x5e},{0x75,0xc2,0x78,0x0d,0x92,0x50}, - {0x6b,0xd3,0x0f,0x64,0xd9,0x0a},{0x69,0xd0,0x06,0x6f,0xd4,0x04}, - {0x6f,0xd5,0x1d,0x72,0xc3,0x16},{0x6d,0xd6,0x14,0x79,0xce,0x18}, - {0x63,0xdf,0x2b,0x48,0xed,0x32},{0x61,0xdc,0x22,0x43,0xe0,0x3c}, - {0x67,0xd9,0x39,0x5e,0xf7,0x2e},{0x65,0xda,0x30,0x55,0xfa,0x20}, - {0x9b,0x5b,0x9a,0x01,0xb7,0xec},{0x99,0x58,0x93,0x0a,0xba,0xe2}, - {0x9f,0x5d,0x88,0x17,0xad,0xf0},{0x9d,0x5e,0x81,0x1c,0xa0,0xfe}, - {0x93,0x57,0xbe,0x2d,0x83,0xd4},{0x91,0x54,0xb7,0x26,0x8e,0xda}, - {0x97,0x51,0xac,0x3b,0x99,0xc8},{0x95,0x52,0xa5,0x30,0x94,0xc6}, - {0x8b,0x43,0xd2,0x59,0xdf,0x9c},{0x89,0x40,0xdb,0x52,0xd2,0x92}, - {0x8f,0x45,0xc0,0x4f,0xc5,0x80},{0x8d,0x46,0xc9,0x44,0xc8,0x8e}, - {0x83,0x4f,0xf6,0x75,0xeb,0xa4},{0x81,0x4c,0xff,0x7e,0xe6,0xaa}, - {0x87,0x49,0xe4,0x63,0xf1,0xb8},{0x85,0x4a,0xed,0x68,0xfc,0xb6}, - {0xbb,0x6b,0x0a,0xb1,0x67,0x0c},{0xb9,0x68,0x03,0xba,0x6a,0x02}, - {0xbf,0x6d,0x18,0xa7,0x7d,0x10},{0xbd,0x6e,0x11,0xac,0x70,0x1e}, - {0xb3,0x67,0x2e,0x9d,0x53,0x34},{0xb1,0x64,0x27,0x96,0x5e,0x3a}, - {0xb7,0x61,0x3c,0x8b,0x49,0x28},{0xb5,0x62,0x35,0x80,0x44,0x26}, - {0xab,0x73,0x42,0xe9,0x0f,0x7c},{0xa9,0x70,0x4b,0xe2,0x02,0x72}, - {0xaf,0x75,0x50,0xff,0x15,0x60},{0xad,0x76,0x59,0xf4,0x18,0x6e}, - {0xa3,0x7f,0x66,0xc5,0x3b,0x44},{0xa1,0x7c,0x6f,0xce,0x36,0x4a}, - {0xa7,0x79,0x74,0xd3,0x21,0x58},{0xa5,0x7a,0x7d,0xd8,0x2c,0x56}, - {0xdb,0x3b,0xa1,0x7a,0x0c,0x37},{0xd9,0x38,0xa8,0x71,0x01,0x39}, - {0xdf,0x3d,0xb3,0x6c,0x16,0x2b},{0xdd,0x3e,0xba,0x67,0x1b,0x25}, - {0xd3,0x37,0x85,0x56,0x38,0x0f},{0xd1,0x34,0x8c,0x5d,0x35,0x01}, - {0xd7,0x31,0x97,0x40,0x22,0x13},{0xd5,0x32,0x9e,0x4b,0x2f,0x1d}, - {0xcb,0x23,0xe9,0x22,0x64,0x47},{0xc9,0x20,0xe0,0x29,0x69,0x49}, - {0xcf,0x25,0xfb,0x34,0x7e,0x5b},{0xcd,0x26,0xf2,0x3f,0x73,0x55}, - {0xc3,0x2f,0xcd,0x0e,0x50,0x7f},{0xc1,0x2c,0xc4,0x05,0x5d,0x71}, - {0xc7,0x29,0xdf,0x18,0x4a,0x63},{0xc5,0x2a,0xd6,0x13,0x47,0x6d}, - {0xfb,0x0b,0x31,0xca,0xdc,0xd7},{0xf9,0x08,0x38,0xc1,0xd1,0xd9}, - {0xff,0x0d,0x23,0xdc,0xc6,0xcb},{0xfd,0x0e,0x2a,0xd7,0xcb,0xc5}, - {0xf3,0x07,0x15,0xe6,0xe8,0xef},{0xf1,0x04,0x1c,0xed,0xe5,0xe1}, - {0xf7,0x01,0x07,0xf0,0xf2,0xf3},{0xf5,0x02,0x0e,0xfb,0xff,0xfd}, - {0xeb,0x13,0x79,0x92,0xb4,0xa7},{0xe9,0x10,0x70,0x99,0xb9,0xa9}, - {0xef,0x15,0x6b,0x84,0xae,0xbb},{0xed,0x16,0x62,0x8f,0xa3,0xb5}, - {0xe3,0x1f,0x5d,0xbe,0x80,0x9f},{0xe1,0x1c,0x54,0xb5,0x8d,0x91}, - {0xe7,0x19,0x4f,0xa8,0x9a,0x83},{0xe5,0x1a,0x46,0xa3,0x97,0x8d} -}; - + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x02, 0x03, 0x09, 0x0b, 0x0d, 0x0e}, {0x04, 0x06, 0x12, 0x16, 0x1a, 0x1c}, + {0x06, 0x05, 0x1b, 0x1d, 0x17, 0x12}, {0x08, 0x0c, 0x24, 0x2c, 0x34, 0x38}, {0x0a, 0x0f, 0x2d, 0x27, 0x39, 0x36}, + {0x0c, 0x0a, 0x36, 0x3a, 0x2e, 0x24}, {0x0e, 0x09, 0x3f, 0x31, 0x23, 0x2a}, {0x10, 0x18, 0x48, 0x58, 0x68, 0x70}, + {0x12, 0x1b, 0x41, 0x53, 0x65, 0x7e}, {0x14, 0x1e, 0x5a, 0x4e, 0x72, 0x6c}, {0x16, 0x1d, 0x53, 0x45, 0x7f, 0x62}, + {0x18, 0x14, 0x6c, 0x74, 0x5c, 0x48}, {0x1a, 0x17, 0x65, 0x7f, 0x51, 0x46}, {0x1c, 0x12, 0x7e, 0x62, 0x46, 0x54}, + {0x1e, 0x11, 0x77, 0x69, 0x4b, 0x5a}, {0x20, 0x30, 0x90, 0xb0, 0xd0, 0xe0}, {0x22, 0x33, 0x99, 0xbb, 0xdd, 0xee}, + {0x24, 0x36, 0x82, 0xa6, 0xca, 0xfc}, {0x26, 0x35, 0x8b, 0xad, 0xc7, 0xf2}, {0x28, 0x3c, 0xb4, 0x9c, 0xe4, 0xd8}, + {0x2a, 0x3f, 0xbd, 0x97, 0xe9, 0xd6}, {0x2c, 0x3a, 0xa6, 0x8a, 0xfe, 0xc4}, {0x2e, 0x39, 0xaf, 0x81, 0xf3, 0xca}, + {0x30, 0x28, 0xd8, 0xe8, 0xb8, 0x90}, {0x32, 0x2b, 0xd1, 0xe3, 0xb5, 0x9e}, {0x34, 0x2e, 0xca, 0xfe, 0xa2, 0x8c}, + {0x36, 0x2d, 0xc3, 0xf5, 0xaf, 0x82}, {0x38, 0x24, 0xfc, 0xc4, 0x8c, 0xa8}, {0x3a, 0x27, 0xf5, 0xcf, 0x81, 0xa6}, + {0x3c, 0x22, 0xee, 0xd2, 0x96, 0xb4}, {0x3e, 0x21, 0xe7, 0xd9, 0x9b, 0xba}, {0x40, 0x60, 0x3b, 0x7b, 0xbb, 0xdb}, + {0x42, 0x63, 0x32, 0x70, 0xb6, 0xd5}, {0x44, 0x66, 0x29, 0x6d, 0xa1, 0xc7}, {0x46, 0x65, 0x20, 0x66, 0xac, 0xc9}, + {0x48, 0x6c, 0x1f, 0x57, 0x8f, 0xe3}, {0x4a, 0x6f, 0x16, 0x5c, 0x82, 0xed}, {0x4c, 0x6a, 0x0d, 0x41, 0x95, 0xff}, + {0x4e, 0x69, 0x04, 0x4a, 0x98, 0xf1}, {0x50, 0x78, 0x73, 0x23, 0xd3, 0xab}, {0x52, 0x7b, 0x7a, 0x28, 0xde, 0xa5}, + {0x54, 0x7e, 0x61, 0x35, 0xc9, 0xb7}, {0x56, 0x7d, 0x68, 0x3e, 0xc4, 0xb9}, {0x58, 0x74, 0x57, 0x0f, 0xe7, 0x93}, + {0x5a, 0x77, 0x5e, 0x04, 0xea, 0x9d}, {0x5c, 0x72, 0x45, 0x19, 0xfd, 0x8f}, {0x5e, 0x71, 0x4c, 0x12, 0xf0, 0x81}, + {0x60, 0x50, 0xab, 0xcb, 0x6b, 0x3b}, {0x62, 0x53, 0xa2, 0xc0, 0x66, 0x35}, {0x64, 0x56, 0xb9, 0xdd, 0x71, 0x27}, + {0x66, 0x55, 0xb0, 0xd6, 0x7c, 0x29}, {0x68, 0x5c, 0x8f, 0xe7, 0x5f, 0x03}, {0x6a, 0x5f, 0x86, 0xec, 0x52, 0x0d}, + {0x6c, 0x5a, 0x9d, 0xf1, 0x45, 0x1f}, {0x6e, 0x59, 0x94, 0xfa, 0x48, 0x11}, {0x70, 0x48, 0xe3, 0x93, 0x03, 0x4b}, + {0x72, 0x4b, 0xea, 0x98, 0x0e, 0x45}, {0x74, 0x4e, 0xf1, 0x85, 0x19, 0x57}, {0x76, 0x4d, 0xf8, 0x8e, 0x14, 0x59}, + {0x78, 0x44, 0xc7, 0xbf, 0x37, 0x73}, {0x7a, 0x47, 0xce, 0xb4, 0x3a, 0x7d}, {0x7c, 0x42, 0xd5, 0xa9, 0x2d, 0x6f}, + {0x7e, 0x41, 0xdc, 0xa2, 0x20, 0x61}, {0x80, 0xc0, 0x76, 0xf6, 0x6d, 0xad}, {0x82, 0xc3, 0x7f, 0xfd, 0x60, 0xa3}, + {0x84, 0xc6, 0x64, 0xe0, 0x77, 0xb1}, {0x86, 0xc5, 0x6d, 0xeb, 0x7a, 0xbf}, {0x88, 0xcc, 0x52, 0xda, 0x59, 0x95}, + {0x8a, 0xcf, 0x5b, 0xd1, 0x54, 0x9b}, {0x8c, 0xca, 0x40, 0xcc, 0x43, 0x89}, {0x8e, 0xc9, 0x49, 0xc7, 0x4e, 0x87}, + {0x90, 0xd8, 0x3e, 0xae, 0x05, 0xdd}, {0x92, 0xdb, 0x37, 0xa5, 0x08, 0xd3}, {0x94, 0xde, 0x2c, 0xb8, 0x1f, 0xc1}, + {0x96, 0xdd, 0x25, 0xb3, 0x12, 0xcf}, {0x98, 0xd4, 0x1a, 0x82, 0x31, 0xe5}, {0x9a, 0xd7, 0x13, 0x89, 0x3c, 0xeb}, + {0x9c, 0xd2, 0x08, 0x94, 0x2b, 0xf9}, {0x9e, 0xd1, 0x01, 0x9f, 0x26, 0xf7}, {0xa0, 0xf0, 0xe6, 0x46, 0xbd, 0x4d}, + {0xa2, 0xf3, 0xef, 0x4d, 0xb0, 0x43}, {0xa4, 0xf6, 0xf4, 0x50, 0xa7, 0x51}, {0xa6, 0xf5, 0xfd, 0x5b, 0xaa, 0x5f}, + {0xa8, 0xfc, 0xc2, 0x6a, 0x89, 0x75}, {0xaa, 0xff, 0xcb, 0x61, 0x84, 0x7b}, {0xac, 0xfa, 0xd0, 0x7c, 0x93, 0x69}, + {0xae, 0xf9, 0xd9, 0x77, 0x9e, 0x67}, {0xb0, 0xe8, 0xae, 0x1e, 0xd5, 0x3d}, {0xb2, 0xeb, 0xa7, 0x15, 0xd8, 0x33}, + {0xb4, 0xee, 0xbc, 0x08, 0xcf, 0x21}, {0xb6, 0xed, 0xb5, 0x03, 0xc2, 0x2f}, {0xb8, 0xe4, 0x8a, 0x32, 0xe1, 0x05}, + {0xba, 0xe7, 0x83, 0x39, 0xec, 0x0b}, {0xbc, 0xe2, 0x98, 0x24, 0xfb, 0x19}, {0xbe, 0xe1, 0x91, 0x2f, 0xf6, 0x17}, + {0xc0, 0xa0, 0x4d, 0x8d, 0xd6, 0x76}, {0xc2, 0xa3, 0x44, 0x86, 0xdb, 0x78}, {0xc4, 0xa6, 0x5f, 0x9b, 0xcc, 0x6a}, + {0xc6, 0xa5, 0x56, 0x90, 0xc1, 0x64}, {0xc8, 0xac, 0x69, 0xa1, 0xe2, 0x4e}, {0xca, 0xaf, 0x60, 0xaa, 0xef, 0x40}, + {0xcc, 0xaa, 0x7b, 0xb7, 0xf8, 0x52}, {0xce, 0xa9, 0x72, 0xbc, 0xf5, 0x5c}, {0xd0, 0xb8, 0x05, 0xd5, 0xbe, 0x06}, + {0xd2, 0xbb, 0x0c, 0xde, 0xb3, 0x08}, {0xd4, 0xbe, 0x17, 0xc3, 0xa4, 0x1a}, {0xd6, 0xbd, 0x1e, 0xc8, 0xa9, 0x14}, + {0xd8, 0xb4, 0x21, 0xf9, 0x8a, 0x3e}, {0xda, 0xb7, 0x28, 0xf2, 0x87, 0x30}, {0xdc, 0xb2, 0x33, 0xef, 0x90, 0x22}, + {0xde, 0xb1, 0x3a, 0xe4, 0x9d, 0x2c}, {0xe0, 0x90, 0xdd, 0x3d, 0x06, 0x96}, {0xe2, 0x93, 0xd4, 0x36, 0x0b, 0x98}, + {0xe4, 0x96, 0xcf, 0x2b, 0x1c, 0x8a}, {0xe6, 0x95, 0xc6, 0x20, 0x11, 0x84}, {0xe8, 0x9c, 0xf9, 0x11, 0x32, 0xae}, + {0xea, 0x9f, 0xf0, 0x1a, 0x3f, 0xa0}, {0xec, 0x9a, 0xeb, 0x07, 0x28, 0xb2}, {0xee, 0x99, 0xe2, 0x0c, 0x25, 0xbc}, + {0xf0, 0x88, 0x95, 0x65, 0x6e, 0xe6}, {0xf2, 0x8b, 0x9c, 0x6e, 0x63, 0xe8}, {0xf4, 0x8e, 0x87, 0x73, 0x74, 0xfa}, + {0xf6, 0x8d, 0x8e, 0x78, 0x79, 0xf4}, {0xf8, 0x84, 0xb1, 0x49, 0x5a, 0xde}, {0xfa, 0x87, 0xb8, 0x42, 0x57, 0xd0}, + {0xfc, 0x82, 0xa3, 0x5f, 0x40, 0xc2}, {0xfe, 0x81, 0xaa, 0x54, 0x4d, 0xcc}, {0x1b, 0x9b, 0xec, 0xf7, 0xda, 0x41}, + {0x19, 0x98, 0xe5, 0xfc, 0xd7, 0x4f}, {0x1f, 0x9d, 0xfe, 0xe1, 0xc0, 0x5d}, {0x1d, 0x9e, 0xf7, 0xea, 0xcd, 0x53}, + {0x13, 0x97, 0xc8, 0xdb, 0xee, 0x79}, {0x11, 0x94, 0xc1, 0xd0, 0xe3, 0x77}, {0x17, 0x91, 0xda, 0xcd, 0xf4, 0x65}, + {0x15, 0x92, 0xd3, 0xc6, 0xf9, 0x6b}, {0x0b, 0x83, 0xa4, 0xaf, 0xb2, 0x31}, {0x09, 0x80, 0xad, 0xa4, 0xbf, 0x3f}, + {0x0f, 0x85, 0xb6, 0xb9, 0xa8, 0x2d}, {0x0d, 0x86, 0xbf, 0xb2, 0xa5, 0x23}, {0x03, 0x8f, 0x80, 0x83, 0x86, 0x09}, + {0x01, 0x8c, 0x89, 0x88, 0x8b, 0x07}, {0x07, 0x89, 0x92, 0x95, 0x9c, 0x15}, {0x05, 0x8a, 0x9b, 0x9e, 0x91, 0x1b}, + {0x3b, 0xab, 0x7c, 0x47, 0x0a, 0xa1}, {0x39, 0xa8, 0x75, 0x4c, 0x07, 0xaf}, {0x3f, 0xad, 0x6e, 0x51, 0x10, 0xbd}, + {0x3d, 0xae, 0x67, 0x5a, 0x1d, 0xb3}, {0x33, 0xa7, 0x58, 0x6b, 0x3e, 0x99}, {0x31, 0xa4, 0x51, 0x60, 0x33, 0x97}, + {0x37, 0xa1, 0x4a, 0x7d, 0x24, 0x85}, {0x35, 0xa2, 0x43, 0x76, 0x29, 0x8b}, {0x2b, 0xb3, 0x34, 0x1f, 0x62, 0xd1}, + {0x29, 0xb0, 0x3d, 0x14, 0x6f, 0xdf}, {0x2f, 0xb5, 0x26, 0x09, 0x78, 0xcd}, {0x2d, 0xb6, 0x2f, 0x02, 0x75, 0xc3}, + {0x23, 0xbf, 0x10, 0x33, 0x56, 0xe9}, {0x21, 0xbc, 0x19, 0x38, 0x5b, 0xe7}, {0x27, 0xb9, 0x02, 0x25, 0x4c, 0xf5}, + {0x25, 0xba, 0x0b, 0x2e, 0x41, 0xfb}, {0x5b, 0xfb, 0xd7, 0x8c, 0x61, 0x9a}, {0x59, 0xf8, 0xde, 0x87, 0x6c, 0x94}, + {0x5f, 0xfd, 0xc5, 0x9a, 0x7b, 0x86}, {0x5d, 0xfe, 0xcc, 0x91, 0x76, 0x88}, {0x53, 0xf7, 0xf3, 0xa0, 0x55, 0xa2}, + {0x51, 0xf4, 0xfa, 0xab, 0x58, 0xac}, {0x57, 0xf1, 0xe1, 0xb6, 0x4f, 0xbe}, {0x55, 0xf2, 0xe8, 0xbd, 0x42, 0xb0}, + {0x4b, 0xe3, 0x9f, 0xd4, 0x09, 0xea}, {0x49, 0xe0, 0x96, 0xdf, 0x04, 0xe4}, {0x4f, 0xe5, 0x8d, 0xc2, 0x13, 0xf6}, + {0x4d, 0xe6, 0x84, 0xc9, 0x1e, 0xf8}, {0x43, 0xef, 0xbb, 0xf8, 0x3d, 0xd2}, {0x41, 0xec, 0xb2, 0xf3, 0x30, 0xdc}, + {0x47, 0xe9, 0xa9, 0xee, 0x27, 0xce}, {0x45, 0xea, 0xa0, 0xe5, 0x2a, 0xc0}, {0x7b, 0xcb, 0x47, 0x3c, 0xb1, 0x7a}, + {0x79, 0xc8, 0x4e, 0x37, 0xbc, 0x74}, {0x7f, 0xcd, 0x55, 0x2a, 0xab, 0x66}, {0x7d, 0xce, 0x5c, 0x21, 0xa6, 0x68}, + {0x73, 0xc7, 0x63, 0x10, 0x85, 0x42}, {0x71, 0xc4, 0x6a, 0x1b, 0x88, 0x4c}, {0x77, 0xc1, 0x71, 0x06, 0x9f, 0x5e}, + {0x75, 0xc2, 0x78, 0x0d, 0x92, 0x50}, {0x6b, 0xd3, 0x0f, 0x64, 0xd9, 0x0a}, {0x69, 0xd0, 0x06, 0x6f, 0xd4, 0x04}, + {0x6f, 0xd5, 0x1d, 0x72, 0xc3, 0x16}, {0x6d, 0xd6, 0x14, 0x79, 0xce, 0x18}, {0x63, 0xdf, 0x2b, 0x48, 0xed, 0x32}, + {0x61, 0xdc, 0x22, 0x43, 0xe0, 0x3c}, {0x67, 0xd9, 0x39, 0x5e, 0xf7, 0x2e}, {0x65, 0xda, 0x30, 0x55, 0xfa, 0x20}, + {0x9b, 0x5b, 0x9a, 0x01, 0xb7, 0xec}, {0x99, 0x58, 0x93, 0x0a, 0xba, 0xe2}, {0x9f, 0x5d, 0x88, 0x17, 0xad, 0xf0}, + {0x9d, 0x5e, 0x81, 0x1c, 0xa0, 0xfe}, {0x93, 0x57, 0xbe, 0x2d, 0x83, 0xd4}, {0x91, 0x54, 0xb7, 0x26, 0x8e, 0xda}, + {0x97, 0x51, 0xac, 0x3b, 0x99, 0xc8}, {0x95, 0x52, 0xa5, 0x30, 0x94, 0xc6}, {0x8b, 0x43, 0xd2, 0x59, 0xdf, 0x9c}, + {0x89, 0x40, 0xdb, 0x52, 0xd2, 0x92}, {0x8f, 0x45, 0xc0, 0x4f, 0xc5, 0x80}, {0x8d, 0x46, 0xc9, 0x44, 0xc8, 0x8e}, + {0x83, 0x4f, 0xf6, 0x75, 0xeb, 0xa4}, {0x81, 0x4c, 0xff, 0x7e, 0xe6, 0xaa}, {0x87, 0x49, 0xe4, 0x63, 0xf1, 0xb8}, + {0x85, 0x4a, 0xed, 0x68, 0xfc, 0xb6}, {0xbb, 0x6b, 0x0a, 0xb1, 0x67, 0x0c}, {0xb9, 0x68, 0x03, 0xba, 0x6a, 0x02}, + {0xbf, 0x6d, 0x18, 0xa7, 0x7d, 0x10}, {0xbd, 0x6e, 0x11, 0xac, 0x70, 0x1e}, {0xb3, 0x67, 0x2e, 0x9d, 0x53, 0x34}, + {0xb1, 0x64, 0x27, 0x96, 0x5e, 0x3a}, {0xb7, 0x61, 0x3c, 0x8b, 0x49, 0x28}, {0xb5, 0x62, 0x35, 0x80, 0x44, 0x26}, + {0xab, 0x73, 0x42, 0xe9, 0x0f, 0x7c}, {0xa9, 0x70, 0x4b, 0xe2, 0x02, 0x72}, {0xaf, 0x75, 0x50, 0xff, 0x15, 0x60}, + {0xad, 0x76, 0x59, 0xf4, 0x18, 0x6e}, {0xa3, 0x7f, 0x66, 0xc5, 0x3b, 0x44}, {0xa1, 0x7c, 0x6f, 0xce, 0x36, 0x4a}, + {0xa7, 0x79, 0x74, 0xd3, 0x21, 0x58}, {0xa5, 0x7a, 0x7d, 0xd8, 0x2c, 0x56}, {0xdb, 0x3b, 0xa1, 0x7a, 0x0c, 0x37}, + {0xd9, 0x38, 0xa8, 0x71, 0x01, 0x39}, {0xdf, 0x3d, 0xb3, 0x6c, 0x16, 0x2b}, {0xdd, 0x3e, 0xba, 0x67, 0x1b, 0x25}, + {0xd3, 0x37, 0x85, 0x56, 0x38, 0x0f}, {0xd1, 0x34, 0x8c, 0x5d, 0x35, 0x01}, {0xd7, 0x31, 0x97, 0x40, 0x22, 0x13}, + {0xd5, 0x32, 0x9e, 0x4b, 0x2f, 0x1d}, {0xcb, 0x23, 0xe9, 0x22, 0x64, 0x47}, {0xc9, 0x20, 0xe0, 0x29, 0x69, 0x49}, + {0xcf, 0x25, 0xfb, 0x34, 0x7e, 0x5b}, {0xcd, 0x26, 0xf2, 0x3f, 0x73, 0x55}, {0xc3, 0x2f, 0xcd, 0x0e, 0x50, 0x7f}, + {0xc1, 0x2c, 0xc4, 0x05, 0x5d, 0x71}, {0xc7, 0x29, 0xdf, 0x18, 0x4a, 0x63}, {0xc5, 0x2a, 0xd6, 0x13, 0x47, 0x6d}, + {0xfb, 0x0b, 0x31, 0xca, 0xdc, 0xd7}, {0xf9, 0x08, 0x38, 0xc1, 0xd1, 0xd9}, {0xff, 0x0d, 0x23, 0xdc, 0xc6, 0xcb}, + {0xfd, 0x0e, 0x2a, 0xd7, 0xcb, 0xc5}, {0xf3, 0x07, 0x15, 0xe6, 0xe8, 0xef}, {0xf1, 0x04, 0x1c, 0xed, 0xe5, 0xe1}, + {0xf7, 0x01, 0x07, 0xf0, 0xf2, 0xf3}, {0xf5, 0x02, 0x0e, 0xfb, 0xff, 0xfd}, {0xeb, 0x13, 0x79, 0x92, 0xb4, 0xa7}, + {0xe9, 0x10, 0x70, 0x99, 0xb9, 0xa9}, {0xef, 0x15, 0x6b, 0x84, 0xae, 0xbb}, {0xed, 0x16, 0x62, 0x8f, 0xa3, 0xb5}, + {0xe3, 0x1f, 0x5d, 0xbe, 0x80, 0x9f}, {0xe1, 0x1c, 0x54, 0xb5, 0x8d, 0x91}, {0xe7, 0x19, 0x4f, 0xa8, 0x9a, 0x83}, + {0xe5, 0x1a, 0x46, 0xa3, 0x97, 0x8d}}; + /*********************** FUNCTION DEFINITIONS ***********************/ // XORs the in and out buffers, storing the result in out. Length is in bytes. -void xor_buf(const BYTE in[], BYTE out[], size_t len) -{ - size_t idx; - - for (idx = 0; idx < len; idx++) - out[idx] ^= in[idx]; +void +xor_buf(const BYTE in[], BYTE out[], size_t len) { + size_t idx; + + for (idx = 0; idx < len; idx++) + out[idx] ^= in[idx]; } - + /******************* * AES - CBC *******************/ -int aes_encrypt_cbc(const BYTE in[], size_t in_len, BYTE out[], const WORD key[], int keysize, const BYTE iv[]) -{ - BYTE buf_in[AES_BLOCK_SIZE], buf_out[AES_BLOCK_SIZE], iv_buf[AES_BLOCK_SIZE]; - int blocks, idx; - - if (in_len % AES_BLOCK_SIZE != 0) - return(FALSE); - - blocks = in_len / AES_BLOCK_SIZE; - - memcpy(iv_buf, iv, AES_BLOCK_SIZE); - - for (idx = 0; idx < blocks; idx++) { - memcpy(buf_in, &in[idx * AES_BLOCK_SIZE], AES_BLOCK_SIZE); - xor_buf(iv_buf, buf_in, AES_BLOCK_SIZE); - aes_encrypt(buf_in, buf_out, key, keysize); - memcpy(&out[idx * AES_BLOCK_SIZE], buf_out, AES_BLOCK_SIZE); - memcpy(iv_buf, buf_out, AES_BLOCK_SIZE); - } - - return(TRUE); +int +aes_encrypt_cbc(const BYTE in[], size_t in_len, BYTE out[], const WORD key[], int keysize, const BYTE iv[]) { + BYTE buf_in[AES_BLOCK_SIZE], buf_out[AES_BLOCK_SIZE], iv_buf[AES_BLOCK_SIZE]; + int blocks, idx; + + if (in_len % AES_BLOCK_SIZE != 0) + return (FALSE); + + blocks = in_len / AES_BLOCK_SIZE; + + memcpy(iv_buf, iv, AES_BLOCK_SIZE); + + for (idx = 0; idx < blocks; idx++) { + memcpy(buf_in, &in[idx * AES_BLOCK_SIZE], AES_BLOCK_SIZE); + xor_buf(iv_buf, buf_in, AES_BLOCK_SIZE); + aes_encrypt(buf_in, buf_out, key, keysize); + memcpy(&out[idx * AES_BLOCK_SIZE], buf_out, AES_BLOCK_SIZE); + memcpy(iv_buf, buf_out, AES_BLOCK_SIZE); + } + + return (TRUE); } - -int aes_encrypt_cbc_mac(const BYTE in[], size_t in_len, BYTE out[], const WORD key[], int keysize, const BYTE iv[]) -{ - BYTE buf_in[AES_BLOCK_SIZE], buf_out[AES_BLOCK_SIZE], iv_buf[AES_BLOCK_SIZE]; - int blocks, idx; - - if (in_len % AES_BLOCK_SIZE != 0) - return(FALSE); - - blocks = in_len / AES_BLOCK_SIZE; - - memcpy(iv_buf, iv, AES_BLOCK_SIZE); - - for (idx = 0; idx < blocks; idx++) { - memcpy(buf_in, &in[idx * AES_BLOCK_SIZE], AES_BLOCK_SIZE); - xor_buf(iv_buf, buf_in, AES_BLOCK_SIZE); - aes_encrypt(buf_in, buf_out, key, keysize); - memcpy(iv_buf, buf_out, AES_BLOCK_SIZE); - // Do not output all encrypted blocks. - } - - memcpy(out, buf_out, AES_BLOCK_SIZE); // Only output the last block. - - return(TRUE); + +int +aes_encrypt_cbc_mac(const BYTE in[], size_t in_len, BYTE out[], const WORD key[], int keysize, const BYTE iv[]) { + BYTE buf_in[AES_BLOCK_SIZE], buf_out[AES_BLOCK_SIZE], iv_buf[AES_BLOCK_SIZE]; + int blocks, idx; + + if (in_len % AES_BLOCK_SIZE != 0) + return (FALSE); + + blocks = in_len / AES_BLOCK_SIZE; + + memcpy(iv_buf, iv, AES_BLOCK_SIZE); + + for (idx = 0; idx < blocks; idx++) { + memcpy(buf_in, &in[idx * AES_BLOCK_SIZE], AES_BLOCK_SIZE); + xor_buf(iv_buf, buf_in, AES_BLOCK_SIZE); + aes_encrypt(buf_in, buf_out, key, keysize); + memcpy(iv_buf, buf_out, AES_BLOCK_SIZE); + // Do not output all encrypted blocks. + } + + memcpy(out, buf_out, AES_BLOCK_SIZE); // Only output the last block. + + return (TRUE); } - -int aes_decrypt_cbc(const BYTE in[], size_t in_len, BYTE out[], const WORD key[], int keysize, const BYTE iv[]) -{ - BYTE buf_in[AES_BLOCK_SIZE], buf_out[AES_BLOCK_SIZE], iv_buf[AES_BLOCK_SIZE]; - int blocks, idx; - - if (in_len % AES_BLOCK_SIZE != 0) - return(FALSE); - - blocks = in_len / AES_BLOCK_SIZE; - - memcpy(iv_buf, iv, AES_BLOCK_SIZE); - - for (idx = 0; idx < blocks; idx++) { - memcpy(buf_in, &in[idx * AES_BLOCK_SIZE], AES_BLOCK_SIZE); - aes_decrypt(buf_in, buf_out, key, keysize); - xor_buf(iv_buf, buf_out, AES_BLOCK_SIZE); - memcpy(&out[idx * AES_BLOCK_SIZE], buf_out, AES_BLOCK_SIZE); - memcpy(iv_buf, buf_in, AES_BLOCK_SIZE); - } - - return(TRUE); + +int +aes_decrypt_cbc(const BYTE in[], size_t in_len, BYTE out[], const WORD key[], int keysize, const BYTE iv[]) { + BYTE buf_in[AES_BLOCK_SIZE], buf_out[AES_BLOCK_SIZE], iv_buf[AES_BLOCK_SIZE]; + int blocks, idx; + + if (in_len % AES_BLOCK_SIZE != 0) + return (FALSE); + + blocks = in_len / AES_BLOCK_SIZE; + + memcpy(iv_buf, iv, AES_BLOCK_SIZE); + + for (idx = 0; idx < blocks; idx++) { + memcpy(buf_in, &in[idx * AES_BLOCK_SIZE], AES_BLOCK_SIZE); + aes_decrypt(buf_in, buf_out, key, keysize); + xor_buf(iv_buf, buf_out, AES_BLOCK_SIZE); + memcpy(&out[idx * AES_BLOCK_SIZE], buf_out, AES_BLOCK_SIZE); + memcpy(iv_buf, buf_in, AES_BLOCK_SIZE); + } + + return (TRUE); } - + /******************* * AES - CTR *******************/ -void increment_iv(BYTE iv[], int counter_size) -{ - int idx; - - // Use counter_size bytes at the end of the IV as the big-endian integer to increment. - for (idx = AES_BLOCK_SIZE - 1; idx >= AES_BLOCK_SIZE - counter_size; idx--) { - iv[idx]++; - if (iv[idx] != 0 || idx == AES_BLOCK_SIZE - counter_size) - break; - } +void +increment_iv(BYTE iv[], int counter_size) { + int idx; + + // Use counter_size bytes at the end of the IV as the big-endian integer to increment. + for (idx = AES_BLOCK_SIZE - 1; idx >= AES_BLOCK_SIZE - counter_size; idx--) { + iv[idx]++; + if (iv[idx] != 0 || idx == AES_BLOCK_SIZE - counter_size) + break; + } } - + // Performs the encryption in-place, the input and output buffers may be the same. // Input may be an arbitrary length (in bytes). -void aes_encrypt_ctr(const BYTE in[], size_t in_len, BYTE out[], const WORD key[], int keysize, const BYTE iv[]) -{ - size_t idx = 0, last_block_length; - BYTE iv_buf[AES_BLOCK_SIZE], out_buf[AES_BLOCK_SIZE]; - - if (in != out) - memcpy(out, in, in_len); - - memcpy(iv_buf, iv, AES_BLOCK_SIZE); - last_block_length = in_len - AES_BLOCK_SIZE; - - if (in_len > AES_BLOCK_SIZE) { - for (idx = 0; idx < last_block_length; idx += AES_BLOCK_SIZE) { - aes_encrypt(iv_buf, out_buf, key, keysize); - xor_buf(out_buf, &out[idx], AES_BLOCK_SIZE); - increment_iv(iv_buf, AES_BLOCK_SIZE); +void +aes_encrypt_ctr(const BYTE in[], size_t in_len, BYTE out[], const WORD key[], int keysize, const BYTE iv[]) { + size_t idx = 0, last_block_length; + BYTE iv_buf[AES_BLOCK_SIZE], out_buf[AES_BLOCK_SIZE]; + + if (in != out) + memcpy(out, in, in_len); + + memcpy(iv_buf, iv, AES_BLOCK_SIZE); + last_block_length = in_len - AES_BLOCK_SIZE; + + if (in_len > AES_BLOCK_SIZE) { + for (idx = 0; idx < last_block_length; idx += AES_BLOCK_SIZE) { + aes_encrypt(iv_buf, out_buf, key, keysize); + xor_buf(out_buf, &out[idx], AES_BLOCK_SIZE); + increment_iv(iv_buf, AES_BLOCK_SIZE); + } } - } - - aes_encrypt(iv_buf, out_buf, key, keysize); - xor_buf(out_buf, &out[idx], in_len - idx); // Use the Most Significant bytes. + + aes_encrypt(iv_buf, out_buf, key, keysize); + xor_buf(out_buf, &out[idx], in_len - idx); // Use the Most Significant bytes. } - -void aes_decrypt_ctr(const BYTE in[], size_t in_len, BYTE out[], const WORD key[], int keysize, const BYTE iv[]) -{ - // CTR encryption is its own inverse function. - aes_encrypt_ctr(in, in_len, out, key, keysize, iv); + +void +aes_decrypt_ctr(const BYTE in[], size_t in_len, BYTE out[], const WORD key[], int keysize, const BYTE iv[]) { + // CTR encryption is its own inverse function. + aes_encrypt_ctr(in, in_len, out, key, keysize, iv); } - + /******************* * AES - CCM *******************/ // out_len = payload_len + assoc_len -int aes_encrypt_ccm(const BYTE payload[], WORD payload_len, const BYTE assoc[], unsigned short assoc_len, - const BYTE nonce[], unsigned short nonce_len, BYTE out[], WORD *out_len, - WORD mac_len, const BYTE key_str[], int keysize) -{ - BYTE temp_iv[AES_BLOCK_SIZE], counter[AES_BLOCK_SIZE], mac[16], *buf; - int end_of_buf, payload_len_store_size; - WORD key[60]; - - if (mac_len != 4 && mac_len != 6 && mac_len != 8 && mac_len != 10 && - mac_len != 12 && mac_len != 14 && mac_len != 16) - return(FALSE); - - if (nonce_len < 7 || nonce_len > 13) - return(FALSE); - - if (assoc_len > 32768 /* = 2^15 */) - return(FALSE); - - buf = (BYTE*)malloc(payload_len + assoc_len + 48 /*Round both payload and associated data up a block size and add an extra block.*/); - if (! buf) - return(FALSE); - - // Prepare the key for usage. - aes_key_setup(key_str, key, keysize); - - // Format the first block of the formatted data. - payload_len_store_size = AES_BLOCK_SIZE - 1 - nonce_len; - ccm_prepare_first_format_blk(buf, assoc_len, payload_len, payload_len_store_size, mac_len, nonce, nonce_len); - end_of_buf = AES_BLOCK_SIZE; - - // Format the Associated Data, aka, assoc[]. - ccm_format_assoc_data(buf, &end_of_buf, assoc, assoc_len); - - // Format the Payload, aka payload[]. - ccm_format_payload_data(buf, &end_of_buf, payload, payload_len); - - // Create the first counter block. - ccm_prepare_first_ctr_blk(counter, nonce, nonce_len, payload_len_store_size); - - // Perform the CBC operation with an IV of zeros on the formatted buffer to calculate the MAC. - memset(temp_iv, 0, AES_BLOCK_SIZE); - aes_encrypt_cbc_mac(buf, end_of_buf, mac, key, keysize, temp_iv); - - // Copy the Payload and MAC to the output buffer. - memcpy(out, payload, payload_len); - memcpy(&out[payload_len], mac, mac_len); - - // Encrypt the Payload with CTR mode with a counter starting at 1. - memcpy(temp_iv, counter, AES_BLOCK_SIZE); - increment_iv(temp_iv, AES_BLOCK_SIZE - 1 - mac_len); // Last argument is the byte size of the counting portion of the counter block. /*BUG?*/ - aes_encrypt_ctr(out, payload_len, out, key, keysize, temp_iv); - - // Encrypt the MAC with CTR mode with a counter starting at 0. - aes_encrypt_ctr(&out[payload_len], mac_len, &out[payload_len], key, keysize, counter); - - free(buf); - *out_len = payload_len + mac_len; - - return(TRUE); -} - -// plaintext_len = ciphertext_len - mac_len -// Needs a flag for whether the MAC matches. -int aes_decrypt_ccm(const BYTE ciphertext[], WORD ciphertext_len, const BYTE assoc[], unsigned short assoc_len, - const BYTE nonce[], unsigned short nonce_len, BYTE plaintext[], WORD *plaintext_len, - WORD mac_len, int *mac_auth, const BYTE key_str[], int keysize) -{ - BYTE temp_iv[AES_BLOCK_SIZE], counter[AES_BLOCK_SIZE], mac[16], mac_buf[16], *buf; - int end_of_buf, plaintext_len_store_size; - WORD key[60]; - - if (ciphertext_len <= mac_len) - return(FALSE); - - buf = (BYTE*)malloc(assoc_len + ciphertext_len /*ciphertext_len = plaintext_len + mac_len*/ + 48); - if (! buf) - return(FALSE); - - // Prepare the key for usage. - aes_key_setup(key_str, key, keysize); - - // Copy the plaintext and MAC to the output buffers. - *plaintext_len = ciphertext_len - mac_len; - plaintext_len_store_size = AES_BLOCK_SIZE - 1 - nonce_len; - memcpy(plaintext, ciphertext, *plaintext_len); - memcpy(mac, &ciphertext[*plaintext_len], mac_len); - - // Prepare the first counter block for use in decryption. - ccm_prepare_first_ctr_blk(counter, nonce, nonce_len, plaintext_len_store_size); - - // Decrypt the Payload with CTR mode with a counter starting at 1. - memcpy(temp_iv, counter, AES_BLOCK_SIZE); - increment_iv(temp_iv, AES_BLOCK_SIZE - 1 - mac_len); // (AES_BLOCK_SIZE - 1 - mac_len) is the byte size of the counting portion of the counter block. - aes_decrypt_ctr(plaintext, *plaintext_len, plaintext, key, keysize, temp_iv); - - // Setting mac_auth to NULL disables the authentication check. - if (mac_auth != NULL) { - // Decrypt the MAC with CTR mode with a counter starting at 0. - aes_decrypt_ctr(mac, mac_len, mac, key, keysize, counter); - +int +aes_encrypt_ccm(const BYTE payload[], WORD payload_len, const BYTE assoc[], unsigned short assoc_len, + const BYTE nonce[], unsigned short nonce_len, BYTE out[], WORD *out_len, WORD mac_len, + const BYTE key_str[], int keysize) { + BYTE temp_iv[AES_BLOCK_SIZE], counter[AES_BLOCK_SIZE], mac[16], *buf; + int end_of_buf, payload_len_store_size; + WORD key[60]; + + if (mac_len != 4 && mac_len != 6 && mac_len != 8 && mac_len != 10 && mac_len != 12 && mac_len != 14 && + mac_len != 16) + return (FALSE); + + if (nonce_len < 7 || nonce_len > 13) + return (FALSE); + + if (assoc_len > 32768 /* = 2^15 */) + return (FALSE); + + buf = (BYTE *)malloc(payload_len + assoc_len + + 48 /*Round both payload and associated data up a block size and add an extra block.*/); + if (!buf) + return (FALSE); + + // Prepare the key for usage. + aes_key_setup(key_str, key, keysize); + // Format the first block of the formatted data. - plaintext_len_store_size = AES_BLOCK_SIZE - 1 - nonce_len; - ccm_prepare_first_format_blk(buf, assoc_len, *plaintext_len, plaintext_len_store_size, mac_len, nonce, nonce_len); + payload_len_store_size = AES_BLOCK_SIZE - 1 - nonce_len; + ccm_prepare_first_format_blk(buf, assoc_len, payload_len, payload_len_store_size, mac_len, nonce, nonce_len); end_of_buf = AES_BLOCK_SIZE; - - // Format the Associated Data into the authentication buffer. + + // Format the Associated Data, aka, assoc[]. ccm_format_assoc_data(buf, &end_of_buf, assoc, assoc_len); - - // Format the Payload into the authentication buffer. - ccm_format_payload_data(buf, &end_of_buf, plaintext, *plaintext_len); - + + // Format the Payload, aka payload[]. + ccm_format_payload_data(buf, &end_of_buf, payload, payload_len); + + // Create the first counter block. + ccm_prepare_first_ctr_blk(counter, nonce, nonce_len, payload_len_store_size); + // Perform the CBC operation with an IV of zeros on the formatted buffer to calculate the MAC. memset(temp_iv, 0, AES_BLOCK_SIZE); - aes_encrypt_cbc_mac(buf, end_of_buf, mac_buf, key, keysize, temp_iv); - - // Compare the calculated MAC against the MAC embedded in the ciphertext to see if they are the same. - if (! memcmp(mac, mac_buf, mac_len)) { - *mac_auth = TRUE; - } - else { - *mac_auth = FALSE; - memset(plaintext, 0, *plaintext_len); + aes_encrypt_cbc_mac(buf, end_of_buf, mac, key, keysize, temp_iv); + + // Copy the Payload and MAC to the output buffer. + memcpy(out, payload, payload_len); + memcpy(&out[payload_len], mac, mac_len); + + // Encrypt the Payload with CTR mode with a counter starting at 1. + memcpy(temp_iv, counter, AES_BLOCK_SIZE); + increment_iv( + temp_iv, + AES_BLOCK_SIZE - 1 - + mac_len); // Last argument is the byte size of the counting portion of the counter block. /*BUG?*/ + aes_encrypt_ctr(out, payload_len, out, key, keysize, temp_iv); + + // Encrypt the MAC with CTR mode with a counter starting at 0. + aes_encrypt_ctr(&out[payload_len], mac_len, &out[payload_len], key, keysize, counter); + + free(buf); + *out_len = payload_len + mac_len; + + return (TRUE); +} + +// plaintext_len = ciphertext_len - mac_len +// Needs a flag for whether the MAC matches. +int +aes_decrypt_ccm(const BYTE ciphertext[], WORD ciphertext_len, const BYTE assoc[], unsigned short assoc_len, + const BYTE nonce[], unsigned short nonce_len, BYTE plaintext[], WORD *plaintext_len, WORD mac_len, + int *mac_auth, const BYTE key_str[], int keysize) { + BYTE temp_iv[AES_BLOCK_SIZE], counter[AES_BLOCK_SIZE], mac[16], mac_buf[16], *buf; + int end_of_buf, plaintext_len_store_size; + WORD key[60]; + + if (ciphertext_len <= mac_len) + return (FALSE); + + buf = (BYTE *)malloc(assoc_len + ciphertext_len /*ciphertext_len = plaintext_len + mac_len*/ + 48); + if (!buf) + return (FALSE); + + // Prepare the key for usage. + aes_key_setup(key_str, key, keysize); + + // Copy the plaintext and MAC to the output buffers. + *plaintext_len = ciphertext_len - mac_len; + plaintext_len_store_size = AES_BLOCK_SIZE - 1 - nonce_len; + memcpy(plaintext, ciphertext, *plaintext_len); + memcpy(mac, &ciphertext[*plaintext_len], mac_len); + + // Prepare the first counter block for use in decryption. + ccm_prepare_first_ctr_blk(counter, nonce, nonce_len, plaintext_len_store_size); + + // Decrypt the Payload with CTR mode with a counter starting at 1. + memcpy(temp_iv, counter, AES_BLOCK_SIZE); + increment_iv(temp_iv, AES_BLOCK_SIZE - 1 - mac_len); // (AES_BLOCK_SIZE - 1 - mac_len) is the byte size of the + // counting portion of the counter block. + aes_decrypt_ctr(plaintext, *plaintext_len, plaintext, key, keysize, temp_iv); + + // Setting mac_auth to NULL disables the authentication check. + if (mac_auth != NULL) { + // Decrypt the MAC with CTR mode with a counter starting at 0. + aes_decrypt_ctr(mac, mac_len, mac, key, keysize, counter); + + // Format the first block of the formatted data. + plaintext_len_store_size = AES_BLOCK_SIZE - 1 - nonce_len; + ccm_prepare_first_format_blk(buf, assoc_len, *plaintext_len, plaintext_len_store_size, mac_len, nonce, + nonce_len); + end_of_buf = AES_BLOCK_SIZE; + + // Format the Associated Data into the authentication buffer. + ccm_format_assoc_data(buf, &end_of_buf, assoc, assoc_len); + + // Format the Payload into the authentication buffer. + ccm_format_payload_data(buf, &end_of_buf, plaintext, *plaintext_len); + + // Perform the CBC operation with an IV of zeros on the formatted buffer to calculate the MAC. + memset(temp_iv, 0, AES_BLOCK_SIZE); + aes_encrypt_cbc_mac(buf, end_of_buf, mac_buf, key, keysize, temp_iv); + + // Compare the calculated MAC against the MAC embedded in the ciphertext to see if they are the same. + if (!memcmp(mac, mac_buf, mac_len)) { + *mac_auth = TRUE; + } else { + *mac_auth = FALSE; + memset(plaintext, 0, *plaintext_len); + } } - } - - free(buf); - - return(TRUE); + + free(buf); + + return (TRUE); } - + // Creates the first counter block. First byte is flags, then the nonce, then the incremented part. -void ccm_prepare_first_ctr_blk(BYTE counter[], const BYTE nonce[], int nonce_len, int payload_len_store_size) -{ - memset(counter, 0, AES_BLOCK_SIZE); - counter[0] = (payload_len_store_size - 1) & 0x07; - memcpy(&counter[1], nonce, nonce_len); +void +ccm_prepare_first_ctr_blk(BYTE counter[], const BYTE nonce[], int nonce_len, int payload_len_store_size) { + memset(counter, 0, AES_BLOCK_SIZE); + counter[0] = (payload_len_store_size - 1) & 0x07; + memcpy(&counter[1], nonce, nonce_len); } - -void ccm_prepare_first_format_blk(BYTE buf[], int assoc_len, int payload_len, int payload_len_store_size, int mac_len, const BYTE nonce[], int nonce_len) -{ - // Set the flags for the first byte of the first block. - buf[0] = ((((mac_len - 2) / 2) & 0x07) << 3) | ((payload_len_store_size - 1) & 0x07); - if (assoc_len > 0) - buf[0] += 0x40; - // Format the rest of the first block, storing the nonce and the size of the payload. - memcpy(&buf[1], nonce, nonce_len); - memset(&buf[1 + nonce_len], 0, AES_BLOCK_SIZE - 1 - nonce_len); - buf[15] = payload_len & 0x000000FF; - buf[14] = (payload_len >> 8) & 0x000000FF; + +void +ccm_prepare_first_format_blk(BYTE buf[], int assoc_len, int payload_len, int payload_len_store_size, int mac_len, + const BYTE nonce[], int nonce_len) { + // Set the flags for the first byte of the first block. + buf[0] = ((((mac_len - 2) / 2) & 0x07) << 3) | ((payload_len_store_size - 1) & 0x07); + if (assoc_len > 0) + buf[0] += 0x40; + // Format the rest of the first block, storing the nonce and the size of the payload. + memcpy(&buf[1], nonce, nonce_len); + memset(&buf[1 + nonce_len], 0, AES_BLOCK_SIZE - 1 - nonce_len); + buf[15] = payload_len & 0x000000FF; + buf[14] = (payload_len >> 8) & 0x000000FF; } - -void ccm_format_assoc_data(BYTE buf[], int *end_of_buf, const BYTE assoc[], int assoc_len) -{ - int pad; - - buf[*end_of_buf + 1] = assoc_len & 0x00FF; - buf[*end_of_buf] = (assoc_len >> 8) & 0x00FF; - *end_of_buf += 2; - memcpy(&buf[*end_of_buf], assoc, assoc_len); - *end_of_buf += assoc_len; - pad = AES_BLOCK_SIZE - (*end_of_buf % AES_BLOCK_SIZE); /*BUG?*/ - memset(&buf[*end_of_buf], 0, pad); - *end_of_buf += pad; + +void +ccm_format_assoc_data(BYTE buf[], int *end_of_buf, const BYTE assoc[], int assoc_len) { + int pad; + + buf[*end_of_buf + 1] = assoc_len & 0x00FF; + buf[*end_of_buf] = (assoc_len >> 8) & 0x00FF; + *end_of_buf += 2; + memcpy(&buf[*end_of_buf], assoc, assoc_len); + *end_of_buf += assoc_len; + pad = AES_BLOCK_SIZE - (*end_of_buf % AES_BLOCK_SIZE); /*BUG?*/ + memset(&buf[*end_of_buf], 0, pad); + *end_of_buf += pad; } - -void ccm_format_payload_data(BYTE buf[], int *end_of_buf, const BYTE payload[], int payload_len) -{ - int pad; - - memcpy(&buf[*end_of_buf], payload, payload_len); - *end_of_buf += payload_len; - pad = *end_of_buf % AES_BLOCK_SIZE; - if (pad != 0) - pad = AES_BLOCK_SIZE - pad; - memset(&buf[*end_of_buf], 0, pad); - *end_of_buf += pad; + +void +ccm_format_payload_data(BYTE buf[], int *end_of_buf, const BYTE payload[], int payload_len) { + int pad; + + memcpy(&buf[*end_of_buf], payload, payload_len); + *end_of_buf += payload_len; + pad = *end_of_buf % AES_BLOCK_SIZE; + if (pad != 0) + pad = AES_BLOCK_SIZE - pad; + memset(&buf[*end_of_buf], 0, pad); + *end_of_buf += pad; } - + /******************* * AES *******************/ ///////////////// // KEY EXPANSION ///////////////// - + // Substitutes a word using the AES S-Box. -WORD SubWord(WORD word) -{ - unsigned int result; - - result = (int)aes_sbox[(word >> 4) & 0x0000000F][word & 0x0000000F]; - result += (int)aes_sbox[(word >> 12) & 0x0000000F][(word >> 8) & 0x0000000F] << 8; - result += (int)aes_sbox[(word >> 20) & 0x0000000F][(word >> 16) & 0x0000000F] << 16; - result += (int)aes_sbox[(word >> 28) & 0x0000000F][(word >> 24) & 0x0000000F] << 24; - return(result); +WORD +SubWord(WORD word) { + unsigned int result; + + result = (int)aes_sbox[(word >> 4) & 0x0000000F][word & 0x0000000F]; + result += (int)aes_sbox[(word >> 12) & 0x0000000F][(word >> 8) & 0x0000000F] << 8; + result += (int)aes_sbox[(word >> 20) & 0x0000000F][(word >> 16) & 0x0000000F] << 16; + result += (int)aes_sbox[(word >> 28) & 0x0000000F][(word >> 24) & 0x0000000F] << 24; + return (result); } - + // Performs the action of generating the keys that will be used in every round of // encryption. "key" is the user-supplied input key, "w" is the output key schedule, // "keysize" is the length in bits of "key", must be 128, 192, or 256. -void aes_key_setup(const BYTE key[], WORD w[], int keysize) -{ - int Nb=4,Nr,Nk,idx; - WORD temp,Rcon[]={0x01000000,0x02000000,0x04000000,0x08000000,0x10000000,0x20000000, - 0x40000000,0x80000000,0x1b000000,0x36000000,0x6c000000,0xd8000000, - 0xab000000,0x4d000000,0x9a000000}; - - switch (keysize) { - case 128: Nr = 10; Nk = 4; break; - case 192: Nr = 12; Nk = 6; break; - case 256: Nr = 14; Nk = 8; break; - default: return; - } - - for (idx=0; idx < Nk; ++idx) { - w[idx] = ((key[4 * idx]) << 24) | ((key[4 * idx + 1]) << 16) | - ((key[4 * idx + 2]) << 8) | ((key[4 * idx + 3])); - } - - for (idx = Nk; idx < Nb * (Nr+1); ++idx) { - temp = w[idx - 1]; - if ((idx % Nk) == 0) - temp = SubWord(KE_ROTWORD(temp)) ^ Rcon[(idx-1)/Nk]; - else if (Nk > 6 && (idx % Nk) == 4) - temp = SubWord(temp); - w[idx] = w[idx-Nk] ^ temp; - } +void +aes_key_setup(const BYTE key[], WORD w[], int keysize) { + int Nb = 4, Nr, Nk, idx; + WORD temp, + Rcon[] = {0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000, + 0x1b000000, 0x36000000, 0x6c000000, 0xd8000000, 0xab000000, 0x4d000000, 0x9a000000}; + + switch (keysize) { + case 128: + Nr = 10; + Nk = 4; + break; + case 192: + Nr = 12; + Nk = 6; + break; + case 256: + Nr = 14; + Nk = 8; + break; + default: + return; + } + + for (idx = 0; idx < Nk; ++idx) { + w[idx] = ((key[4 * idx]) << 24) | ((key[4 * idx + 1]) << 16) | ((key[4 * idx + 2]) << 8) | + ((key[4 * idx + 3])); + } + + for (idx = Nk; idx < Nb * (Nr + 1); ++idx) { + temp = w[idx - 1]; + if ((idx % Nk) == 0) + temp = SubWord(KE_ROTWORD(temp)) ^ Rcon[(idx - 1) / Nk]; + else if (Nk > 6 && (idx % Nk) == 4) + temp = SubWord(temp); + w[idx] = w[idx - Nk] ^ temp; + } } - + ///////////////// // ADD ROUND KEY ///////////////// - + // Performs the AddRoundKey step. Each round has its own pre-generated 16-byte key in the // form of 4 integers (the "w" array). Each integer is XOR'd by one column of the state. // Also performs the job of InvAddRoundKey(); since the function is a simple XOR process, // it is its own inverse. -void AddRoundKey(BYTE state[][4], const WORD w[]) -{ - BYTE subkey[4]; - - // memcpy(subkey,&w[idx],4); // Not accurate for big endian machines - // Subkey 1 - subkey[0] = w[0] >> 24; - subkey[1] = w[0] >> 16; - subkey[2] = w[0] >> 8; - subkey[3] = w[0]; - state[0][0] ^= subkey[0]; - state[1][0] ^= subkey[1]; - state[2][0] ^= subkey[2]; - state[3][0] ^= subkey[3]; - // Subkey 2 - subkey[0] = w[1] >> 24; - subkey[1] = w[1] >> 16; - subkey[2] = w[1] >> 8; - subkey[3] = w[1]; - state[0][1] ^= subkey[0]; - state[1][1] ^= subkey[1]; - state[2][1] ^= subkey[2]; - state[3][1] ^= subkey[3]; - // Subkey 3 - subkey[0] = w[2] >> 24; - subkey[1] = w[2] >> 16; - subkey[2] = w[2] >> 8; - subkey[3] = w[2]; - state[0][2] ^= subkey[0]; - state[1][2] ^= subkey[1]; - state[2][2] ^= subkey[2]; - state[3][2] ^= subkey[3]; - // Subkey 4 - subkey[0] = w[3] >> 24; - subkey[1] = w[3] >> 16; - subkey[2] = w[3] >> 8; - subkey[3] = w[3]; - state[0][3] ^= subkey[0]; - state[1][3] ^= subkey[1]; - state[2][3] ^= subkey[2]; - state[3][3] ^= subkey[3]; +void +AddRoundKey(BYTE state[][4], const WORD w[]) { + BYTE subkey[4]; + + // memcpy(subkey,&w[idx],4); // Not accurate for big endian machines + // Subkey 1 + subkey[0] = w[0] >> 24; + subkey[1] = w[0] >> 16; + subkey[2] = w[0] >> 8; + subkey[3] = w[0]; + state[0][0] ^= subkey[0]; + state[1][0] ^= subkey[1]; + state[2][0] ^= subkey[2]; + state[3][0] ^= subkey[3]; + // Subkey 2 + subkey[0] = w[1] >> 24; + subkey[1] = w[1] >> 16; + subkey[2] = w[1] >> 8; + subkey[3] = w[1]; + state[0][1] ^= subkey[0]; + state[1][1] ^= subkey[1]; + state[2][1] ^= subkey[2]; + state[3][1] ^= subkey[3]; + // Subkey 3 + subkey[0] = w[2] >> 24; + subkey[1] = w[2] >> 16; + subkey[2] = w[2] >> 8; + subkey[3] = w[2]; + state[0][2] ^= subkey[0]; + state[1][2] ^= subkey[1]; + state[2][2] ^= subkey[2]; + state[3][2] ^= subkey[3]; + // Subkey 4 + subkey[0] = w[3] >> 24; + subkey[1] = w[3] >> 16; + subkey[2] = w[3] >> 8; + subkey[3] = w[3]; + state[0][3] ^= subkey[0]; + state[1][3] ^= subkey[1]; + state[2][3] ^= subkey[2]; + state[3][3] ^= subkey[3]; } - + ///////////////// // (Inv)SubBytes ///////////////// - + // Performs the SubBytes step. All bytes in the state are substituted with a // pre-calculated value from a lookup table. -void SubBytes(BYTE state[][4]) -{ - state[0][0] = aes_sbox[state[0][0] >> 4][state[0][0] & 0x0F]; - state[0][1] = aes_sbox[state[0][1] >> 4][state[0][1] & 0x0F]; - state[0][2] = aes_sbox[state[0][2] >> 4][state[0][2] & 0x0F]; - state[0][3] = aes_sbox[state[0][3] >> 4][state[0][3] & 0x0F]; - state[1][0] = aes_sbox[state[1][0] >> 4][state[1][0] & 0x0F]; - state[1][1] = aes_sbox[state[1][1] >> 4][state[1][1] & 0x0F]; - state[1][2] = aes_sbox[state[1][2] >> 4][state[1][2] & 0x0F]; - state[1][3] = aes_sbox[state[1][3] >> 4][state[1][3] & 0x0F]; - state[2][0] = aes_sbox[state[2][0] >> 4][state[2][0] & 0x0F]; - state[2][1] = aes_sbox[state[2][1] >> 4][state[2][1] & 0x0F]; - state[2][2] = aes_sbox[state[2][2] >> 4][state[2][2] & 0x0F]; - state[2][3] = aes_sbox[state[2][3] >> 4][state[2][3] & 0x0F]; - state[3][0] = aes_sbox[state[3][0] >> 4][state[3][0] & 0x0F]; - state[3][1] = aes_sbox[state[3][1] >> 4][state[3][1] & 0x0F]; - state[3][2] = aes_sbox[state[3][2] >> 4][state[3][2] & 0x0F]; - state[3][3] = aes_sbox[state[3][3] >> 4][state[3][3] & 0x0F]; +void +SubBytes(BYTE state[][4]) { + state[0][0] = aes_sbox[state[0][0] >> 4][state[0][0] & 0x0F]; + state[0][1] = aes_sbox[state[0][1] >> 4][state[0][1] & 0x0F]; + state[0][2] = aes_sbox[state[0][2] >> 4][state[0][2] & 0x0F]; + state[0][3] = aes_sbox[state[0][3] >> 4][state[0][3] & 0x0F]; + state[1][0] = aes_sbox[state[1][0] >> 4][state[1][0] & 0x0F]; + state[1][1] = aes_sbox[state[1][1] >> 4][state[1][1] & 0x0F]; + state[1][2] = aes_sbox[state[1][2] >> 4][state[1][2] & 0x0F]; + state[1][3] = aes_sbox[state[1][3] >> 4][state[1][3] & 0x0F]; + state[2][0] = aes_sbox[state[2][0] >> 4][state[2][0] & 0x0F]; + state[2][1] = aes_sbox[state[2][1] >> 4][state[2][1] & 0x0F]; + state[2][2] = aes_sbox[state[2][2] >> 4][state[2][2] & 0x0F]; + state[2][3] = aes_sbox[state[2][3] >> 4][state[2][3] & 0x0F]; + state[3][0] = aes_sbox[state[3][0] >> 4][state[3][0] & 0x0F]; + state[3][1] = aes_sbox[state[3][1] >> 4][state[3][1] & 0x0F]; + state[3][2] = aes_sbox[state[3][2] >> 4][state[3][2] & 0x0F]; + state[3][3] = aes_sbox[state[3][3] >> 4][state[3][3] & 0x0F]; } - -void InvSubBytes(BYTE state[][4]) -{ - state[0][0] = aes_invsbox[state[0][0] >> 4][state[0][0] & 0x0F]; - state[0][1] = aes_invsbox[state[0][1] >> 4][state[0][1] & 0x0F]; - state[0][2] = aes_invsbox[state[0][2] >> 4][state[0][2] & 0x0F]; - state[0][3] = aes_invsbox[state[0][3] >> 4][state[0][3] & 0x0F]; - state[1][0] = aes_invsbox[state[1][0] >> 4][state[1][0] & 0x0F]; - state[1][1] = aes_invsbox[state[1][1] >> 4][state[1][1] & 0x0F]; - state[1][2] = aes_invsbox[state[1][2] >> 4][state[1][2] & 0x0F]; - state[1][3] = aes_invsbox[state[1][3] >> 4][state[1][3] & 0x0F]; - state[2][0] = aes_invsbox[state[2][0] >> 4][state[2][0] & 0x0F]; - state[2][1] = aes_invsbox[state[2][1] >> 4][state[2][1] & 0x0F]; - state[2][2] = aes_invsbox[state[2][2] >> 4][state[2][2] & 0x0F]; - state[2][3] = aes_invsbox[state[2][3] >> 4][state[2][3] & 0x0F]; - state[3][0] = aes_invsbox[state[3][0] >> 4][state[3][0] & 0x0F]; - state[3][1] = aes_invsbox[state[3][1] >> 4][state[3][1] & 0x0F]; - state[3][2] = aes_invsbox[state[3][2] >> 4][state[3][2] & 0x0F]; - state[3][3] = aes_invsbox[state[3][3] >> 4][state[3][3] & 0x0F]; + +void +InvSubBytes(BYTE state[][4]) { + state[0][0] = aes_invsbox[state[0][0] >> 4][state[0][0] & 0x0F]; + state[0][1] = aes_invsbox[state[0][1] >> 4][state[0][1] & 0x0F]; + state[0][2] = aes_invsbox[state[0][2] >> 4][state[0][2] & 0x0F]; + state[0][3] = aes_invsbox[state[0][3] >> 4][state[0][3] & 0x0F]; + state[1][0] = aes_invsbox[state[1][0] >> 4][state[1][0] & 0x0F]; + state[1][1] = aes_invsbox[state[1][1] >> 4][state[1][1] & 0x0F]; + state[1][2] = aes_invsbox[state[1][2] >> 4][state[1][2] & 0x0F]; + state[1][3] = aes_invsbox[state[1][3] >> 4][state[1][3] & 0x0F]; + state[2][0] = aes_invsbox[state[2][0] >> 4][state[2][0] & 0x0F]; + state[2][1] = aes_invsbox[state[2][1] >> 4][state[2][1] & 0x0F]; + state[2][2] = aes_invsbox[state[2][2] >> 4][state[2][2] & 0x0F]; + state[2][3] = aes_invsbox[state[2][3] >> 4][state[2][3] & 0x0F]; + state[3][0] = aes_invsbox[state[3][0] >> 4][state[3][0] & 0x0F]; + state[3][1] = aes_invsbox[state[3][1] >> 4][state[3][1] & 0x0F]; + state[3][2] = aes_invsbox[state[3][2] >> 4][state[3][2] & 0x0F]; + state[3][3] = aes_invsbox[state[3][3] >> 4][state[3][3] & 0x0F]; } - + ///////////////// // (Inv)ShiftRows ///////////////// - + // Performs the ShiftRows step. All rows are shifted cylindrically to the left. -void ShiftRows(BYTE state[][4]) -{ - int t; - - // Shift left by 1 - t = state[1][0]; - state[1][0] = state[1][1]; - state[1][1] = state[1][2]; - state[1][2] = state[1][3]; - state[1][3] = t; - // Shift left by 2 - t = state[2][0]; - state[2][0] = state[2][2]; - state[2][2] = t; - t = state[2][1]; - state[2][1] = state[2][3]; - state[2][3] = t; - // Shift left by 3 - t = state[3][0]; - state[3][0] = state[3][3]; - state[3][3] = state[3][2]; - state[3][2] = state[3][1]; - state[3][1] = t; +void +ShiftRows(BYTE state[][4]) { + int t; + + // Shift left by 1 + t = state[1][0]; + state[1][0] = state[1][1]; + state[1][1] = state[1][2]; + state[1][2] = state[1][3]; + state[1][3] = t; + // Shift left by 2 + t = state[2][0]; + state[2][0] = state[2][2]; + state[2][2] = t; + t = state[2][1]; + state[2][1] = state[2][3]; + state[2][3] = t; + // Shift left by 3 + t = state[3][0]; + state[3][0] = state[3][3]; + state[3][3] = state[3][2]; + state[3][2] = state[3][1]; + state[3][1] = t; } - + // All rows are shifted cylindrically to the right. -void InvShiftRows(BYTE state[][4]) -{ - int t; - - // Shift right by 1 - t = state[1][3]; - state[1][3] = state[1][2]; - state[1][2] = state[1][1]; - state[1][1] = state[1][0]; - state[1][0] = t; - // Shift right by 2 - t = state[2][3]; - state[2][3] = state[2][1]; - state[2][1] = t; - t = state[2][2]; - state[2][2] = state[2][0]; - state[2][0] = t; - // Shift right by 3 - t = state[3][3]; - state[3][3] = state[3][0]; - state[3][0] = state[3][1]; - state[3][1] = state[3][2]; - state[3][2] = t; +void +InvShiftRows(BYTE state[][4]) { + int t; + + // Shift right by 1 + t = state[1][3]; + state[1][3] = state[1][2]; + state[1][2] = state[1][1]; + state[1][1] = state[1][0]; + state[1][0] = t; + // Shift right by 2 + t = state[2][3]; + state[2][3] = state[2][1]; + state[2][1] = t; + t = state[2][2]; + state[2][2] = state[2][0]; + state[2][0] = t; + // Shift right by 3 + t = state[3][3]; + state[3][3] = state[3][0]; + state[3][0] = state[3][1]; + state[3][1] = state[3][2]; + state[3][2] = t; } - + ///////////////// // (Inv)MixColumns ///////////////// - + // Performs the MixColums step. The state is multiplied by itself using matrix // multiplication in a Galios Field 2^8. All multiplication is pre-computed in a table. // Addition is equivilent to XOR. (Must always make a copy of the column as the original // values will be destoyed.) -void MixColumns(BYTE state[][4]) -{ - BYTE col[4]; - - // Column 1 - col[0] = state[0][0]; - col[1] = state[1][0]; - col[2] = state[2][0]; - col[3] = state[3][0]; - state[0][0] = gf_mul[col[0]][0]; - state[0][0] ^= gf_mul[col[1]][1]; - state[0][0] ^= col[2]; - state[0][0] ^= col[3]; - state[1][0] = col[0]; - state[1][0] ^= gf_mul[col[1]][0]; - state[1][0] ^= gf_mul[col[2]][1]; - state[1][0] ^= col[3]; - state[2][0] = col[0]; - state[2][0] ^= col[1]; - state[2][0] ^= gf_mul[col[2]][0]; - state[2][0] ^= gf_mul[col[3]][1]; - state[3][0] = gf_mul[col[0]][1]; - state[3][0] ^= col[1]; - state[3][0] ^= col[2]; - state[3][0] ^= gf_mul[col[3]][0]; - // Column 2 - col[0] = state[0][1]; - col[1] = state[1][1]; - col[2] = state[2][1]; - col[3] = state[3][1]; - state[0][1] = gf_mul[col[0]][0]; - state[0][1] ^= gf_mul[col[1]][1]; - state[0][1] ^= col[2]; - state[0][1] ^= col[3]; - state[1][1] = col[0]; - state[1][1] ^= gf_mul[col[1]][0]; - state[1][1] ^= gf_mul[col[2]][1]; - state[1][1] ^= col[3]; - state[2][1] = col[0]; - state[2][1] ^= col[1]; - state[2][1] ^= gf_mul[col[2]][0]; - state[2][1] ^= gf_mul[col[3]][1]; - state[3][1] = gf_mul[col[0]][1]; - state[3][1] ^= col[1]; - state[3][1] ^= col[2]; - state[3][1] ^= gf_mul[col[3]][0]; - // Column 3 - col[0] = state[0][2]; - col[1] = state[1][2]; - col[2] = state[2][2]; - col[3] = state[3][2]; - state[0][2] = gf_mul[col[0]][0]; - state[0][2] ^= gf_mul[col[1]][1]; - state[0][2] ^= col[2]; - state[0][2] ^= col[3]; - state[1][2] = col[0]; - state[1][2] ^= gf_mul[col[1]][0]; - state[1][2] ^= gf_mul[col[2]][1]; - state[1][2] ^= col[3]; - state[2][2] = col[0]; - state[2][2] ^= col[1]; - state[2][2] ^= gf_mul[col[2]][0]; - state[2][2] ^= gf_mul[col[3]][1]; - state[3][2] = gf_mul[col[0]][1]; - state[3][2] ^= col[1]; - state[3][2] ^= col[2]; - state[3][2] ^= gf_mul[col[3]][0]; - // Column 4 - col[0] = state[0][3]; - col[1] = state[1][3]; - col[2] = state[2][3]; - col[3] = state[3][3]; - state[0][3] = gf_mul[col[0]][0]; - state[0][3] ^= gf_mul[col[1]][1]; - state[0][3] ^= col[2]; - state[0][3] ^= col[3]; - state[1][3] = col[0]; - state[1][3] ^= gf_mul[col[1]][0]; - state[1][3] ^= gf_mul[col[2]][1]; - state[1][3] ^= col[3]; - state[2][3] = col[0]; - state[2][3] ^= col[1]; - state[2][3] ^= gf_mul[col[2]][0]; - state[2][3] ^= gf_mul[col[3]][1]; - state[3][3] = gf_mul[col[0]][1]; - state[3][3] ^= col[1]; - state[3][3] ^= col[2]; - state[3][3] ^= gf_mul[col[3]][0]; +void +MixColumns(BYTE state[][4]) { + BYTE col[4]; + + // Column 1 + col[0] = state[0][0]; + col[1] = state[1][0]; + col[2] = state[2][0]; + col[3] = state[3][0]; + state[0][0] = gf_mul[col[0]][0]; + state[0][0] ^= gf_mul[col[1]][1]; + state[0][0] ^= col[2]; + state[0][0] ^= col[3]; + state[1][0] = col[0]; + state[1][0] ^= gf_mul[col[1]][0]; + state[1][0] ^= gf_mul[col[2]][1]; + state[1][0] ^= col[3]; + state[2][0] = col[0]; + state[2][0] ^= col[1]; + state[2][0] ^= gf_mul[col[2]][0]; + state[2][0] ^= gf_mul[col[3]][1]; + state[3][0] = gf_mul[col[0]][1]; + state[3][0] ^= col[1]; + state[3][0] ^= col[2]; + state[3][0] ^= gf_mul[col[3]][0]; + // Column 2 + col[0] = state[0][1]; + col[1] = state[1][1]; + col[2] = state[2][1]; + col[3] = state[3][1]; + state[0][1] = gf_mul[col[0]][0]; + state[0][1] ^= gf_mul[col[1]][1]; + state[0][1] ^= col[2]; + state[0][1] ^= col[3]; + state[1][1] = col[0]; + state[1][1] ^= gf_mul[col[1]][0]; + state[1][1] ^= gf_mul[col[2]][1]; + state[1][1] ^= col[3]; + state[2][1] = col[0]; + state[2][1] ^= col[1]; + state[2][1] ^= gf_mul[col[2]][0]; + state[2][1] ^= gf_mul[col[3]][1]; + state[3][1] = gf_mul[col[0]][1]; + state[3][1] ^= col[1]; + state[3][1] ^= col[2]; + state[3][1] ^= gf_mul[col[3]][0]; + // Column 3 + col[0] = state[0][2]; + col[1] = state[1][2]; + col[2] = state[2][2]; + col[3] = state[3][2]; + state[0][2] = gf_mul[col[0]][0]; + state[0][2] ^= gf_mul[col[1]][1]; + state[0][2] ^= col[2]; + state[0][2] ^= col[3]; + state[1][2] = col[0]; + state[1][2] ^= gf_mul[col[1]][0]; + state[1][2] ^= gf_mul[col[2]][1]; + state[1][2] ^= col[3]; + state[2][2] = col[0]; + state[2][2] ^= col[1]; + state[2][2] ^= gf_mul[col[2]][0]; + state[2][2] ^= gf_mul[col[3]][1]; + state[3][2] = gf_mul[col[0]][1]; + state[3][2] ^= col[1]; + state[3][2] ^= col[2]; + state[3][2] ^= gf_mul[col[3]][0]; + // Column 4 + col[0] = state[0][3]; + col[1] = state[1][3]; + col[2] = state[2][3]; + col[3] = state[3][3]; + state[0][3] = gf_mul[col[0]][0]; + state[0][3] ^= gf_mul[col[1]][1]; + state[0][3] ^= col[2]; + state[0][3] ^= col[3]; + state[1][3] = col[0]; + state[1][3] ^= gf_mul[col[1]][0]; + state[1][3] ^= gf_mul[col[2]][1]; + state[1][3] ^= col[3]; + state[2][3] = col[0]; + state[2][3] ^= col[1]; + state[2][3] ^= gf_mul[col[2]][0]; + state[2][3] ^= gf_mul[col[3]][1]; + state[3][3] = gf_mul[col[0]][1]; + state[3][3] ^= col[1]; + state[3][3] ^= col[2]; + state[3][3] ^= gf_mul[col[3]][0]; } - -void InvMixColumns(BYTE state[][4]) -{ - BYTE col[4]; - - // Column 1 - col[0] = state[0][0]; - col[1] = state[1][0]; - col[2] = state[2][0]; - col[3] = state[3][0]; - state[0][0] = gf_mul[col[0]][5]; - state[0][0] ^= gf_mul[col[1]][3]; - state[0][0] ^= gf_mul[col[2]][4]; - state[0][0] ^= gf_mul[col[3]][2]; - state[1][0] = gf_mul[col[0]][2]; - state[1][0] ^= gf_mul[col[1]][5]; - state[1][0] ^= gf_mul[col[2]][3]; - state[1][0] ^= gf_mul[col[3]][4]; - state[2][0] = gf_mul[col[0]][4]; - state[2][0] ^= gf_mul[col[1]][2]; - state[2][0] ^= gf_mul[col[2]][5]; - state[2][0] ^= gf_mul[col[3]][3]; - state[3][0] = gf_mul[col[0]][3]; - state[3][0] ^= gf_mul[col[1]][4]; - state[3][0] ^= gf_mul[col[2]][2]; - state[3][0] ^= gf_mul[col[3]][5]; - // Column 2 - col[0] = state[0][1]; - col[1] = state[1][1]; - col[2] = state[2][1]; - col[3] = state[3][1]; - state[0][1] = gf_mul[col[0]][5]; - state[0][1] ^= gf_mul[col[1]][3]; - state[0][1] ^= gf_mul[col[2]][4]; - state[0][1] ^= gf_mul[col[3]][2]; - state[1][1] = gf_mul[col[0]][2]; - state[1][1] ^= gf_mul[col[1]][5]; - state[1][1] ^= gf_mul[col[2]][3]; - state[1][1] ^= gf_mul[col[3]][4]; - state[2][1] = gf_mul[col[0]][4]; - state[2][1] ^= gf_mul[col[1]][2]; - state[2][1] ^= gf_mul[col[2]][5]; - state[2][1] ^= gf_mul[col[3]][3]; - state[3][1] = gf_mul[col[0]][3]; - state[3][1] ^= gf_mul[col[1]][4]; - state[3][1] ^= gf_mul[col[2]][2]; - state[3][1] ^= gf_mul[col[3]][5]; - // Column 3 - col[0] = state[0][2]; - col[1] = state[1][2]; - col[2] = state[2][2]; - col[3] = state[3][2]; - state[0][2] = gf_mul[col[0]][5]; - state[0][2] ^= gf_mul[col[1]][3]; - state[0][2] ^= gf_mul[col[2]][4]; - state[0][2] ^= gf_mul[col[3]][2]; - state[1][2] = gf_mul[col[0]][2]; - state[1][2] ^= gf_mul[col[1]][5]; - state[1][2] ^= gf_mul[col[2]][3]; - state[1][2] ^= gf_mul[col[3]][4]; - state[2][2] = gf_mul[col[0]][4]; - state[2][2] ^= gf_mul[col[1]][2]; - state[2][2] ^= gf_mul[col[2]][5]; - state[2][2] ^= gf_mul[col[3]][3]; - state[3][2] = gf_mul[col[0]][3]; - state[3][2] ^= gf_mul[col[1]][4]; - state[3][2] ^= gf_mul[col[2]][2]; - state[3][2] ^= gf_mul[col[3]][5]; - // Column 4 - col[0] = state[0][3]; - col[1] = state[1][3]; - col[2] = state[2][3]; - col[3] = state[3][3]; - state[0][3] = gf_mul[col[0]][5]; - state[0][3] ^= gf_mul[col[1]][3]; - state[0][3] ^= gf_mul[col[2]][4]; - state[0][3] ^= gf_mul[col[3]][2]; - state[1][3] = gf_mul[col[0]][2]; - state[1][3] ^= gf_mul[col[1]][5]; - state[1][3] ^= gf_mul[col[2]][3]; - state[1][3] ^= gf_mul[col[3]][4]; - state[2][3] = gf_mul[col[0]][4]; - state[2][3] ^= gf_mul[col[1]][2]; - state[2][3] ^= gf_mul[col[2]][5]; - state[2][3] ^= gf_mul[col[3]][3]; - state[3][3] = gf_mul[col[0]][3]; - state[3][3] ^= gf_mul[col[1]][4]; - state[3][3] ^= gf_mul[col[2]][2]; - state[3][3] ^= gf_mul[col[3]][5]; + +void +InvMixColumns(BYTE state[][4]) { + BYTE col[4]; + + // Column 1 + col[0] = state[0][0]; + col[1] = state[1][0]; + col[2] = state[2][0]; + col[3] = state[3][0]; + state[0][0] = gf_mul[col[0]][5]; + state[0][0] ^= gf_mul[col[1]][3]; + state[0][0] ^= gf_mul[col[2]][4]; + state[0][0] ^= gf_mul[col[3]][2]; + state[1][0] = gf_mul[col[0]][2]; + state[1][0] ^= gf_mul[col[1]][5]; + state[1][0] ^= gf_mul[col[2]][3]; + state[1][0] ^= gf_mul[col[3]][4]; + state[2][0] = gf_mul[col[0]][4]; + state[2][0] ^= gf_mul[col[1]][2]; + state[2][0] ^= gf_mul[col[2]][5]; + state[2][0] ^= gf_mul[col[3]][3]; + state[3][0] = gf_mul[col[0]][3]; + state[3][0] ^= gf_mul[col[1]][4]; + state[3][0] ^= gf_mul[col[2]][2]; + state[3][0] ^= gf_mul[col[3]][5]; + // Column 2 + col[0] = state[0][1]; + col[1] = state[1][1]; + col[2] = state[2][1]; + col[3] = state[3][1]; + state[0][1] = gf_mul[col[0]][5]; + state[0][1] ^= gf_mul[col[1]][3]; + state[0][1] ^= gf_mul[col[2]][4]; + state[0][1] ^= gf_mul[col[3]][2]; + state[1][1] = gf_mul[col[0]][2]; + state[1][1] ^= gf_mul[col[1]][5]; + state[1][1] ^= gf_mul[col[2]][3]; + state[1][1] ^= gf_mul[col[3]][4]; + state[2][1] = gf_mul[col[0]][4]; + state[2][1] ^= gf_mul[col[1]][2]; + state[2][1] ^= gf_mul[col[2]][5]; + state[2][1] ^= gf_mul[col[3]][3]; + state[3][1] = gf_mul[col[0]][3]; + state[3][1] ^= gf_mul[col[1]][4]; + state[3][1] ^= gf_mul[col[2]][2]; + state[3][1] ^= gf_mul[col[3]][5]; + // Column 3 + col[0] = state[0][2]; + col[1] = state[1][2]; + col[2] = state[2][2]; + col[3] = state[3][2]; + state[0][2] = gf_mul[col[0]][5]; + state[0][2] ^= gf_mul[col[1]][3]; + state[0][2] ^= gf_mul[col[2]][4]; + state[0][2] ^= gf_mul[col[3]][2]; + state[1][2] = gf_mul[col[0]][2]; + state[1][2] ^= gf_mul[col[1]][5]; + state[1][2] ^= gf_mul[col[2]][3]; + state[1][2] ^= gf_mul[col[3]][4]; + state[2][2] = gf_mul[col[0]][4]; + state[2][2] ^= gf_mul[col[1]][2]; + state[2][2] ^= gf_mul[col[2]][5]; + state[2][2] ^= gf_mul[col[3]][3]; + state[3][2] = gf_mul[col[0]][3]; + state[3][2] ^= gf_mul[col[1]][4]; + state[3][2] ^= gf_mul[col[2]][2]; + state[3][2] ^= gf_mul[col[3]][5]; + // Column 4 + col[0] = state[0][3]; + col[1] = state[1][3]; + col[2] = state[2][3]; + col[3] = state[3][3]; + state[0][3] = gf_mul[col[0]][5]; + state[0][3] ^= gf_mul[col[1]][3]; + state[0][3] ^= gf_mul[col[2]][4]; + state[0][3] ^= gf_mul[col[3]][2]; + state[1][3] = gf_mul[col[0]][2]; + state[1][3] ^= gf_mul[col[1]][5]; + state[1][3] ^= gf_mul[col[2]][3]; + state[1][3] ^= gf_mul[col[3]][4]; + state[2][3] = gf_mul[col[0]][4]; + state[2][3] ^= gf_mul[col[1]][2]; + state[2][3] ^= gf_mul[col[2]][5]; + state[2][3] ^= gf_mul[col[3]][3]; + state[3][3] = gf_mul[col[0]][3]; + state[3][3] ^= gf_mul[col[1]][4]; + state[3][3] ^= gf_mul[col[2]][2]; + state[3][3] ^= gf_mul[col[3]][5]; } - + ///////////////// // (En/De)Crypt ///////////////// - -void aes_encrypt(const BYTE in[], BYTE out[], const WORD key[], int keysize) -{ - BYTE state[4][4]; - - // Copy input array (should be 16 bytes long) to a matrix (sequential bytes are ordered - // by row, not col) called "state" for processing. - // *** Implementation note: The official AES documentation references the state by - // column, then row. Accessing an element in C requires row then column. Thus, all state - // references in AES must have the column and row indexes reversed for C implementation. - state[0][0] = in[0]; - state[1][0] = in[1]; - state[2][0] = in[2]; - state[3][0] = in[3]; - state[0][1] = in[4]; - state[1][1] = in[5]; - state[2][1] = in[6]; - state[3][1] = in[7]; - state[0][2] = in[8]; - state[1][2] = in[9]; - state[2][2] = in[10]; - state[3][2] = in[11]; - state[0][3] = in[12]; - state[1][3] = in[13]; - state[2][3] = in[14]; - state[3][3] = in[15]; - - // Perform the necessary number of rounds. The round key is added first. - // The last round does not perform the MixColumns step. - AddRoundKey(state,&key[0]); - SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[4]); - SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[8]); - SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[12]); - SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[16]); - SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[20]); - SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[24]); - SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[28]); - SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[32]); - SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[36]); - if (keysize != 128) { - SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[40]); - SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[44]); - if (keysize != 192) { - SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[48]); - SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[52]); - SubBytes(state); ShiftRows(state); AddRoundKey(state,&key[56]); - } - else { - SubBytes(state); ShiftRows(state); AddRoundKey(state,&key[48]); + +void +aes_encrypt(const BYTE in[], BYTE out[], const WORD key[], int keysize) { + BYTE state[4][4]; + + // Copy input array (should be 16 bytes long) to a matrix (sequential bytes are ordered + // by row, not col) called "state" for processing. + // *** Implementation note: The official AES documentation references the state by + // column, then row. Accessing an element in C requires row then column. Thus, all state + // references in AES must have the column and row indexes reversed for C implementation. + state[0][0] = in[0]; + state[1][0] = in[1]; + state[2][0] = in[2]; + state[3][0] = in[3]; + state[0][1] = in[4]; + state[1][1] = in[5]; + state[2][1] = in[6]; + state[3][1] = in[7]; + state[0][2] = in[8]; + state[1][2] = in[9]; + state[2][2] = in[10]; + state[3][2] = in[11]; + state[0][3] = in[12]; + state[1][3] = in[13]; + state[2][3] = in[14]; + state[3][3] = in[15]; + + // Perform the necessary number of rounds. The round key is added first. + // The last round does not perform the MixColumns step. + AddRoundKey(state, &key[0]); + SubBytes(state); + ShiftRows(state); + MixColumns(state); + AddRoundKey(state, &key[4]); + SubBytes(state); + ShiftRows(state); + MixColumns(state); + AddRoundKey(state, &key[8]); + SubBytes(state); + ShiftRows(state); + MixColumns(state); + AddRoundKey(state, &key[12]); + SubBytes(state); + ShiftRows(state); + MixColumns(state); + AddRoundKey(state, &key[16]); + SubBytes(state); + ShiftRows(state); + MixColumns(state); + AddRoundKey(state, &key[20]); + SubBytes(state); + ShiftRows(state); + MixColumns(state); + AddRoundKey(state, &key[24]); + SubBytes(state); + ShiftRows(state); + MixColumns(state); + AddRoundKey(state, &key[28]); + SubBytes(state); + ShiftRows(state); + MixColumns(state); + AddRoundKey(state, &key[32]); + SubBytes(state); + ShiftRows(state); + MixColumns(state); + AddRoundKey(state, &key[36]); + if (keysize != 128) { + SubBytes(state); + ShiftRows(state); + MixColumns(state); + AddRoundKey(state, &key[40]); + SubBytes(state); + ShiftRows(state); + MixColumns(state); + AddRoundKey(state, &key[44]); + if (keysize != 192) { + SubBytes(state); + ShiftRows(state); + MixColumns(state); + AddRoundKey(state, &key[48]); + SubBytes(state); + ShiftRows(state); + MixColumns(state); + AddRoundKey(state, &key[52]); + SubBytes(state); + ShiftRows(state); + AddRoundKey(state, &key[56]); + } else { + SubBytes(state); + ShiftRows(state); + AddRoundKey(state, &key[48]); + } + } else { + SubBytes(state); + ShiftRows(state); + AddRoundKey(state, &key[40]); } - } - else { - SubBytes(state); ShiftRows(state); AddRoundKey(state,&key[40]); - } - - // Copy the state to the output array. - out[0] = state[0][0]; - out[1] = state[1][0]; - out[2] = state[2][0]; - out[3] = state[3][0]; - out[4] = state[0][1]; - out[5] = state[1][1]; - out[6] = state[2][1]; - out[7] = state[3][1]; - out[8] = state[0][2]; - out[9] = state[1][2]; - out[10] = state[2][2]; - out[11] = state[3][2]; - out[12] = state[0][3]; - out[13] = state[1][3]; - out[14] = state[2][3]; - out[15] = state[3][3]; + + // Copy the state to the output array. + out[0] = state[0][0]; + out[1] = state[1][0]; + out[2] = state[2][0]; + out[3] = state[3][0]; + out[4] = state[0][1]; + out[5] = state[1][1]; + out[6] = state[2][1]; + out[7] = state[3][1]; + out[8] = state[0][2]; + out[9] = state[1][2]; + out[10] = state[2][2]; + out[11] = state[3][2]; + out[12] = state[0][3]; + out[13] = state[1][3]; + out[14] = state[2][3]; + out[15] = state[3][3]; } - -void aes_decrypt(const BYTE in[], BYTE out[], const WORD key[], int keysize) -{ - BYTE state[4][4]; - - // Copy the input to the state. - state[0][0] = in[0]; - state[1][0] = in[1]; - state[2][0] = in[2]; - state[3][0] = in[3]; - state[0][1] = in[4]; - state[1][1] = in[5]; - state[2][1] = in[6]; - state[3][1] = in[7]; - state[0][2] = in[8]; - state[1][2] = in[9]; - state[2][2] = in[10]; - state[3][2] = in[11]; - state[0][3] = in[12]; - state[1][3] = in[13]; - state[2][3] = in[14]; - state[3][3] = in[15]; - - // Perform the necessary number of rounds. The round key is added first. - // The last round does not perform the MixColumns step. - if (keysize > 128) { - if (keysize > 192) { - AddRoundKey(state,&key[56]); - InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[52]);InvMixColumns(state); - InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[48]);InvMixColumns(state); - } - else { - AddRoundKey(state,&key[48]); + +void +aes_decrypt(const BYTE in[], BYTE out[], const WORD key[], int keysize) { + BYTE state[4][4]; + + // Copy the input to the state. + state[0][0] = in[0]; + state[1][0] = in[1]; + state[2][0] = in[2]; + state[3][0] = in[3]; + state[0][1] = in[4]; + state[1][1] = in[5]; + state[2][1] = in[6]; + state[3][1] = in[7]; + state[0][2] = in[8]; + state[1][2] = in[9]; + state[2][2] = in[10]; + state[3][2] = in[11]; + state[0][3] = in[12]; + state[1][3] = in[13]; + state[2][3] = in[14]; + state[3][3] = in[15]; + + // Perform the necessary number of rounds. The round key is added first. + // The last round does not perform the MixColumns step. + if (keysize > 128) { + if (keysize > 192) { + AddRoundKey(state, &key[56]); + InvShiftRows(state); + InvSubBytes(state); + AddRoundKey(state, &key[52]); + InvMixColumns(state); + InvShiftRows(state); + InvSubBytes(state); + AddRoundKey(state, &key[48]); + InvMixColumns(state); + } else { + AddRoundKey(state, &key[48]); + } + InvShiftRows(state); + InvSubBytes(state); + AddRoundKey(state, &key[44]); + InvMixColumns(state); + InvShiftRows(state); + InvSubBytes(state); + AddRoundKey(state, &key[40]); + InvMixColumns(state); + } else { + AddRoundKey(state, &key[40]); } - InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[44]);InvMixColumns(state); - InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[40]);InvMixColumns(state); - } - else { - AddRoundKey(state,&key[40]); - } - InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[36]);InvMixColumns(state); - InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[32]);InvMixColumns(state); - InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[28]);InvMixColumns(state); - InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[24]);InvMixColumns(state); - InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[20]);InvMixColumns(state); - InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[16]);InvMixColumns(state); - InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[12]);InvMixColumns(state); - InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[8]);InvMixColumns(state); - InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[4]);InvMixColumns(state); - InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[0]); - - // Copy the state to the output array. - out[0] = state[0][0]; - out[1] = state[1][0]; - out[2] = state[2][0]; - out[3] = state[3][0]; - out[4] = state[0][1]; - out[5] = state[1][1]; - out[6] = state[2][1]; - out[7] = state[3][1]; - out[8] = state[0][2]; - out[9] = state[1][2]; - out[10] = state[2][2]; - out[11] = state[3][2]; - out[12] = state[0][3]; - out[13] = state[1][3]; - out[14] = state[2][3]; - out[15] = state[3][3]; + InvShiftRows(state); + InvSubBytes(state); + AddRoundKey(state, &key[36]); + InvMixColumns(state); + InvShiftRows(state); + InvSubBytes(state); + AddRoundKey(state, &key[32]); + InvMixColumns(state); + InvShiftRows(state); + InvSubBytes(state); + AddRoundKey(state, &key[28]); + InvMixColumns(state); + InvShiftRows(state); + InvSubBytes(state); + AddRoundKey(state, &key[24]); + InvMixColumns(state); + InvShiftRows(state); + InvSubBytes(state); + AddRoundKey(state, &key[20]); + InvMixColumns(state); + InvShiftRows(state); + InvSubBytes(state); + AddRoundKey(state, &key[16]); + InvMixColumns(state); + InvShiftRows(state); + InvSubBytes(state); + AddRoundKey(state, &key[12]); + InvMixColumns(state); + InvShiftRows(state); + InvSubBytes(state); + AddRoundKey(state, &key[8]); + InvMixColumns(state); + InvShiftRows(state); + InvSubBytes(state); + AddRoundKey(state, &key[4]); + InvMixColumns(state); + InvShiftRows(state); + InvSubBytes(state); + AddRoundKey(state, &key[0]); + + // Copy the state to the output array. + out[0] = state[0][0]; + out[1] = state[1][0]; + out[2] = state[2][0]; + out[3] = state[3][0]; + out[4] = state[0][1]; + out[5] = state[1][1]; + out[6] = state[2][1]; + out[7] = state[3][1]; + out[8] = state[0][2]; + out[9] = state[1][2]; + out[10] = state[2][2]; + out[11] = state[3][2]; + out[12] = state[0][3]; + out[13] = state[1][3]; + out[14] = state[2][3]; + out[15] = state[3][3]; } - + /******************* ** AES DEBUGGING FUNCTIONS *******************/ @@ -1125,18 +1192,18 @@ void aes_decrypt(const BYTE in[], BYTE out[], const WORD key[], int keysize) void print_state(BYTE state[][4]) { int idx,idx2; - + for (idx=0; idx < 4; idx++) for (idx2=0; idx2 < 4; idx2++) printf("%02x",state[idx2][idx]); printf("\n"); } - + // This prints the key (4 consecutive ints) used for a given round as a linear hex string. void print_rnd_key(WORD key[]) { int idx; - + for (idx=0; idx < 4; idx++) printf("%08x",key[idx]); printf("\n"); diff --git a/examples/aes_decrypt/aes.h b/examples/aes_decrypt/aes.h index 1121c1629..2df6c3a1c 100644 --- a/examples/aes_decrypt/aes.h +++ b/examples/aes_decrypt/aes.h @@ -5,9 +5,9 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside - * 2016-2017 Hewlett Packard Enterprise Development LP + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside + * 2016-2019 Hewlett Packard Enterprise Development LP * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -43,120 +43,134 @@ * Copyright: * Disclaimer: This code is presented "as is" without any guarantees. * Details: Defines the API for the corresponding AES implementation. -* Source: https://github.com/B-Con/crypto-algorithms +* Source: https://github.com/B-Con/crypto-algorithms *********************************************************************/ - -#ifndef AES_H -#define AES_H - + +#ifndef _AES_H_ +#define _AES_H_ + /*************************** HEADER FILES ***************************/ #include - + /****************************** MACROS ******************************/ -#define AES_BLOCK_SIZE 16 // AES operates on 16 bytes at a time - +#define AES_BLOCK_SIZE 16 // AES operates on 16 bytes at a time + /**************************** DATA TYPES ****************************/ -typedef unsigned char BYTE; // 8-bit byte -typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines - +typedef unsigned char BYTE; // 8-bit byte +typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines + /*********************** FUNCTION DECLARATIONS **********************/ /////////////////// // AES /////////////////// // Key setup must be done before any AES en/de-cryption functions can be used. -void aes_key_setup(const BYTE key[], // The key, must be 128, 192, or 256 bits - WORD w[], // Output key schedule to be used later - int keysize); // Bit length of the key, 128, 192, or 256 - -void aes_encrypt(const BYTE in[], // 16 bytes of plaintext - BYTE out[], // 16 bytes of ciphertext - const WORD key[], // From the key setup - int keysize); // Bit length of the key, 128, 192, or 256 - -void aes_decrypt(const BYTE in[], // 16 bytes of ciphertext - BYTE out[], // 16 bytes of plaintext - const WORD key[], // From the key setup - int keysize); // Bit length of the key, 128, 192, or 256 - +void +aes_key_setup(const BYTE key[], // The key, must be 128, 192, or 256 bits + WORD w[], // Output key schedule to be used later + int keysize); // Bit length of the key, 128, 192, or 256 + +void +aes_encrypt(const BYTE in[], // 16 bytes of plaintext + BYTE out[], // 16 bytes of ciphertext + const WORD key[], // From the key setup + int keysize); // Bit length of the key, 128, 192, or 256 + +void +aes_decrypt(const BYTE in[], // 16 bytes of ciphertext + BYTE out[], // 16 bytes of plaintext + const WORD key[], // From the key setup + int keysize); // Bit length of the key, 128, 192, or 256 + /////////////////// // AES - CBC /////////////////// -int aes_encrypt_cbc(const BYTE in[], // Plaintext - size_t in_len, // Must be a multiple of AES_BLOCK_SIZE - BYTE out[], // Ciphertext, same length as plaintext - const WORD key[], // From the key setup - int keysize, // Bit length of the key, 128, 192, or 256 - const BYTE iv[]); // IV, must be AES_BLOCK_SIZE bytes long - +int +aes_encrypt_cbc(const BYTE in[], // Plaintext + size_t in_len, // Must be a multiple of AES_BLOCK_SIZE + BYTE out[], // Ciphertext, same length as plaintext + const WORD key[], // From the key setup + int keysize, // Bit length of the key, 128, 192, or 256 + const BYTE iv[]); // IV, must be AES_BLOCK_SIZE bytes long + // Only output the CBC-MAC of the input. -int aes_encrypt_cbc_mac(const BYTE in[], // plaintext - size_t in_len, // Must be a multiple of AES_BLOCK_SIZE - BYTE out[], // Output MAC - const WORD key[], // From the key setup - int keysize, // Bit length of the key, 128, 192, or 256 - const BYTE iv[]); // IV, must be AES_BLOCK_SIZE bytes long +int +aes_encrypt_cbc_mac(const BYTE in[], // plaintext + size_t in_len, // Must be a multiple of AES_BLOCK_SIZE + BYTE out[], // Output MAC + const WORD key[], // From the key setup + int keysize, // Bit length of the key, 128, 192, or 256 + const BYTE iv[]); // IV, must be AES_BLOCK_SIZE bytes long // Only output the CBC-MAC of the input. -int aes_decrypt_cbc(const BYTE in[], // plaintext - size_t in_len, // Must be a multiple of AES_BLOCK_SIZE - BYTE out[], // Output MAC - const WORD key[], // From the key setup - int keysize, // Bit length of the key, 128, 192, or 256 - const BYTE iv[]); // IV, must be AES_BLOCK_SIZE bytes long - +int +aes_decrypt_cbc(const BYTE in[], // plaintext + size_t in_len, // Must be a multiple of AES_BLOCK_SIZE + BYTE out[], // Output MAC + const WORD key[], // From the key setup + int keysize, // Bit length of the key, 128, 192, or 256 + const BYTE iv[]); // IV, must be AES_BLOCK_SIZE bytes long + /////////////////// // AES - CTR /////////////////// -void increment_iv(BYTE iv[], // Must be a multiple of AES_BLOCK_SIZE - int counter_size); // Bytes of the IV used for counting (low end) - -void aes_encrypt_ctr(const BYTE in[], // Plaintext - size_t in_len, // Any byte length - BYTE out[], // Ciphertext, same length as plaintext - const WORD key[], // From the key setup - int keysize, // Bit length of the key, 128, 192, or 256 - const BYTE iv[]); // IV, must be AES_BLOCK_SIZE bytes long - -void aes_decrypt_ctr(const BYTE in[], // Ciphertext - size_t in_len, // Any byte length - BYTE out[], // Plaintext, same length as ciphertext - const WORD key[], // From the key setup - int keysize, // Bit length of the key, 128, 192, or 256 - const BYTE iv[]); // IV, must be AES_BLOCK_SIZE bytes long - +void +increment_iv(BYTE iv[], // Must be a multiple of AES_BLOCK_SIZE + int counter_size); // Bytes of the IV used for counting (low end) + +void +aes_encrypt_ctr(const BYTE in[], // Plaintext + size_t in_len, // Any byte length + BYTE out[], // Ciphertext, same length as plaintext + const WORD key[], // From the key setup + int keysize, // Bit length of the key, 128, 192, or 256 + const BYTE iv[]); // IV, must be AES_BLOCK_SIZE bytes long + +void +aes_decrypt_ctr(const BYTE in[], // Ciphertext + size_t in_len, // Any byte length + BYTE out[], // Plaintext, same length as ciphertext + const WORD key[], // From the key setup + int keysize, // Bit length of the key, 128, 192, or 256 + const BYTE iv[]); // IV, must be AES_BLOCK_SIZE bytes long + /////////////////// // AES - CCM /////////////////// // Returns True if the input parameters do not violate any constraint. -int aes_encrypt_ccm(const BYTE plaintext[], // IN - Plaintext. - WORD plaintext_len, // IN - Plaintext length. - const BYTE associated_data[], // IN - Associated Data included in authentication, but not encryption. - unsigned short associated_data_len, // IN - Associated Data length in bytes. - const BYTE nonce[], // IN - The Nonce to be used for encryption. - unsigned short nonce_len, // IN - Nonce length in bytes. - BYTE ciphertext[], // OUT - Ciphertext, a concatination of the plaintext and the MAC. - WORD *ciphertext_len, // OUT - The length of the ciphertext, always plaintext_len + mac_len. - WORD mac_len, // IN - The desired length of the MAC, must be 4, 6, 8, 10, 12, 14, or 16. - const BYTE key[], // IN - The AES key for encryption. - int keysize); // IN - The length of the key in bits. Valid values are 128, 192, 256. - +int +aes_encrypt_ccm(const BYTE plaintext[], // IN - Plaintext. + WORD plaintext_len, // IN - Plaintext length. + const BYTE associated_data[], // IN - Associated Data included in authentication, but not encryption. + unsigned short associated_data_len, // IN - Associated Data length in bytes. + const BYTE nonce[], // IN - The Nonce to be used for encryption. + unsigned short nonce_len, // IN - Nonce length in bytes. + BYTE ciphertext[], // OUT - Ciphertext, a concatination of the plaintext and the MAC. + WORD *ciphertext_len, // OUT - The length of the ciphertext, always plaintext_len + mac_len. + WORD mac_len, // IN - The desired length of the MAC, must be 4, 6, 8, 10, 12, 14, or 16. + const BYTE key[], // IN - The AES key for encryption. + int keysize); // IN - The length of the key in bits. Valid values are 128, 192, 256. + // Returns True if the input parameters do not violate any constraint. // Use mac_auth to ensure decryption/validation was preformed correctly. // If authentication does not succeed, the plaintext is zeroed out. To overwride // this, call with mac_auth = NULL. The proper proceedure is to decrypt with // authentication enabled (mac_auth != NULL) and make a second call to that // ignores authentication explicitly if the first call failes. -int aes_decrypt_ccm(const BYTE ciphertext[], // IN - Ciphertext, the concatination of encrypted plaintext and MAC. - WORD ciphertext_len, // IN - Ciphertext length in bytes. - const BYTE assoc[], // IN - The Associated Data, required for authentication. - unsigned short assoc_len, // IN - Associated Data length in bytes. - const BYTE nonce[], // IN - The Nonce to use for decryption, same one as for encryption. - unsigned short nonce_len, // IN - Nonce length in bytes. - BYTE plaintext[], // OUT - The plaintext that was decrypted. Will need to be large enough to hold ciphertext_len - mac_len. - WORD *plaintext_len, // OUT - Length in bytes of the output plaintext, always ciphertext_len - mac_len . - WORD mac_len, // IN - The length of the MAC that was calculated. - int *mac_auth, // OUT - TRUE if authentication succeeded, FALSE if it did not. NULL pointer will ignore the authentication. - const BYTE key[], // IN - The AES key for decryption. - int keysize); // IN - The length of the key in BITS. Valid values are 128, 192, 256. - -#endif // AES_H +int +aes_decrypt_ccm( + const BYTE ciphertext[], // IN - Ciphertext, the concatination of encrypted plaintext and MAC. + WORD ciphertext_len, // IN - Ciphertext length in bytes. + const BYTE assoc[], // IN - The Associated Data, required for authentication. + unsigned short assoc_len, // IN - Associated Data length in bytes. + const BYTE nonce[], // IN - The Nonce to use for decryption, same one as for encryption. + unsigned short nonce_len, // IN - Nonce length in bytes. + BYTE plaintext[], // OUT - The plaintext that was decrypted. Will need to be large enough to hold ciphertext_len - + // mac_len. + WORD *plaintext_len, // OUT - Length in bytes of the output plaintext, always ciphertext_len - mac_len . + WORD mac_len, // IN - The length of the MAC that was calculated. + int *mac_auth, // OUT - TRUE if authentication succeeded, FALSE if it did not. NULL pointer will ignore the + // authentication. + const BYTE key[], // IN - The AES key for decryption. + int keysize); // IN - The length of the key in BITS. Valid values are 128, 192, 256. + +#endif // _AES_H_ diff --git a/examples/aes_decrypt/aesdecrypt.c b/examples/aes_decrypt/aesdecrypt.c index c36a46ec9..3ada41e5f 100644 --- a/examples/aes_decrypt/aesdecrypt.c +++ b/examples/aes_decrypt/aesdecrypt.c @@ -5,9 +5,9 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside - * 2016-2017 Hewlett Packard Enterprise Development LP + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside + * 2016-2019 Hewlett Packard Enterprise Development LP * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -39,46 +39,39 @@ * aesdecrypt.c - Decrypts UDP packets encrypted with AES ********************************************************************/ -#include -#include -#include +#include +#include #include #include -#include -#include +#include +#include #include -#include #include +#include +#include #include -#include +#include #include +#include #include -#include +#include "aes.h" #include "onvm_nflib.h" #include "onvm_pkt_helper.h" -#include "aes.h" #define NF_TAG "aes_encrypt" -/* Struct that contains information about this NF */ -struct onvm_nf_info *nf_info; - /* number of package between each print */ static uint32_t print_delay = 1000000; - static uint32_t destination; /* AES encryption parameters */ -BYTE key[1][32] = { - {0x60,0x3d,0xeb,0x10,0x15,0xca,0x71,0xbe,0x2b,0x73,0xae,0xf0,0x85,0x7d,0x77,0x81,0x1f,0x35,0x2c,0x07,0x3b,0x61,0x08,0xd7,0x2d,0x98,0x10,0xa3,0x09,0x14,0xdf,0xf4} -}; -BYTE iv[1][16] = { - {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f} -}; -WORD key_schedule[60]; //word Schedule +BYTE key[1][32] = {{0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, + 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4}}; +BYTE iv[1][16] = {{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}}; +WORD key_schedule[60]; // word Schedule /* * Print a usage message @@ -102,27 +95,27 @@ parse_app_args(int argc, char *argv[], const char *progname) { while ((c = getopt(argc, argv, "d:p:")) != -1) { switch (c) { - case 'd': - destination = strtoul(optarg, NULL, 10); - dst_flag = 1; - break; - case 'p': - print_delay = strtoul(optarg, NULL, 10); - break; - case '?': - usage(progname); - if (optopt == 'd') - RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); - else if (optopt == 'p') - RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); - else if (isprint(optopt)) - RTE_LOG(INFO, APP, "Unknown option `-%c'.\n", optopt); - else - RTE_LOG(INFO, APP, "Unknown option character `\\x%x'.\n", optopt); - return -1; - default: - usage(progname); - return -1; + case 'd': + destination = strtoul(optarg, NULL, 10); + dst_flag = 1; + break; + case 'p': + print_delay = strtoul(optarg, NULL, 10); + break; + case '?': + usage(progname); + if (optopt == 'd') + RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); + else if (optopt == 'p') + RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); + else if (isprint(optopt)) + RTE_LOG(INFO, APP, "Unknown option `-%c'.\n", optopt); + else + RTE_LOG(INFO, APP, "Unknown option character `\\x%x'.\n", optopt); + return -1; + default: + usage(progname); + return -1; } } @@ -141,11 +134,11 @@ parse_app_args(int argc, char *argv[], const char *progname) { * than one lcore enabled. */ static void -do_stats_display(struct rte_mbuf* pkt) { - const char clr[] = { 27, '[', '2', 'J', '\0' }; - const char topLeft[] = { 27, '[', '1', ';', '1', 'H', '\0' }; +do_stats_display(struct rte_mbuf *pkt) { + const char clr[] = {27, '[', '2', 'J', '\0'}; + const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0'}; static uint64_t pkt_process = 0; - struct ipv4_hdr* ip; + struct ipv4_hdr *ip; pkt_process += print_delay; @@ -156,55 +149,56 @@ do_stats_display(struct rte_mbuf* pkt) { printf("-----\n"); printf("Port : %d\n", pkt->port); printf("Size : %d\n", pkt->pkt_len); - printf("N° : %"PRIu64"\n", pkt_process); + printf("N° : %" PRIu64 "\n", pkt_process); printf("\n\n"); ip = onvm_pkt_ipv4_hdr(pkt); if (ip != NULL) { - struct udp_hdr *udp; + struct udp_hdr *udp; onvm_pkt_print(pkt); - /* Check if we have a valid UDP packet */ - udp = onvm_pkt_udp_hdr(pkt); - if (udp != NULL) { - uint8_t * pkt_data; - pkt_data = ((uint8_t *) udp) + sizeof(struct udp_hdr); - printf("Payload : %.32s\n", pkt_data); - } + /* Check if we have a valid UDP packet */ + udp = onvm_pkt_udp_hdr(pkt); + if (udp != NULL) { + uint8_t *pkt_data; + pkt_data = ((uint8_t *)udp) + sizeof(struct udp_hdr); + printf("Payload : %.32s\n", pkt_data); + } } else { printf("No IP4 header found\n"); } } static int -packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, __attribute__((unused)) struct onvm_nf_info *nf_info) { - struct udp_hdr *udp; +packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, + __attribute__((unused)) struct onvm_nf_local_ctx *nf_local_ctx) { + struct udp_hdr *udp; static uint32_t counter = 0; - /* Check if we have a valid UDP packet */ - udp = onvm_pkt_udp_hdr(pkt); - if (udp != NULL) { - uint8_t * pkt_data; - uint8_t * eth; - uint16_t plen; - uint16_t hlen; - - /* Get at the payload */ - pkt_data = ((uint8_t *) udp) + sizeof(struct udp_hdr); - /* Calculate length */ - eth = rte_pktmbuf_mtod(pkt, uint8_t *); - hlen = pkt_data - eth; - plen = pkt->pkt_len - hlen; - - /* Encrypt. */ - /* IV should change with every packet, but we don't have any - * way to send it to the other side. */ - aes_decrypt_ctr(pkt_data, plen, pkt_data, key_schedule, 256, iv[0]); - if (counter == 0) { - printf("Decrypted %d bytes at offset %d (%ld)\n", - plen, hlen, sizeof(struct ether_hdr) + sizeof(struct ipv4_hdr) + sizeof(struct udp_hdr)); - } - } + /* Check if we have a valid UDP packet */ + udp = onvm_pkt_udp_hdr(pkt); + if (udp != NULL) { + uint8_t *pkt_data; + uint8_t *eth; + uint16_t plen; + uint16_t hlen; + + /* Get at the payload */ + pkt_data = ((uint8_t *)udp) + sizeof(struct udp_hdr); + /* Calculate length */ + eth = rte_pktmbuf_mtod(pkt, uint8_t *); + hlen = pkt_data - eth; + plen = pkt->pkt_len - hlen; + + /* Encrypt. */ + /* IV should change with every packet, but we don't have any + * way to send it to the other side. */ + aes_decrypt_ctr(pkt_data, plen, pkt_data, key_schedule, 256, iv[0]); + if (counter == 0) { + printf("Decrypted %d bytes at offset %d (%ld)\n", plen, hlen, + sizeof(struct ether_hdr) + sizeof(struct ipv4_hdr) + sizeof(struct udp_hdr)); + } + } if (++counter == print_delay) { do_stats_display(pkt); @@ -216,26 +210,43 @@ packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, __attribute__(( return 0; } - -int main(int argc, char *argv[]) { +int +main(int argc, char *argv[]) { int arg_offset; - + struct onvm_nf_local_ctx *nf_local_ctx; + struct onvm_nf_function_table *nf_function_table; const char *progname = argv[0]; - if ((arg_offset = onvm_nflib_init(argc, argv, NF_TAG, &nf_info)) < 0) - return -1; + nf_local_ctx = onvm_nflib_init_nf_local_ctx(); + onvm_nflib_start_signal_handler(nf_local_ctx, NULL); + + nf_function_table = onvm_nflib_init_nf_function_table(); + nf_function_table->pkt_handler = &packet_handler; + + if ((arg_offset = onvm_nflib_init(argc, argv, NF_TAG, nf_local_ctx, nf_function_table)) < 0) { + onvm_nflib_stop(nf_local_ctx); + if (arg_offset == ONVM_SIGNAL_TERMINATION) { + printf("Exiting due to user termination\n"); + return 0; + } else { + rte_exit(EXIT_FAILURE, "Failed ONVM init\n"); + } + } + argc -= arg_offset; argv += arg_offset; if (parse_app_args(argc, argv, progname) < 0) { - onvm_nflib_stop(nf_info); + onvm_nflib_stop(nf_local_ctx); rte_exit(EXIT_FAILURE, "Invalid command-line arguments\n"); } - /* Initialise encryption engine. Key should be configurable. */ - aes_key_setup(key[0], key_schedule, 256); + /* Initialise encryption engine. Key should be configurable. */ + aes_key_setup(key[0], key_schedule, 256); + + onvm_nflib_run(nf_local_ctx); - onvm_nflib_run(nf_info, &packet_handler); + onvm_nflib_stop(nf_local_ctx); printf("If we reach here, program is ending\n"); return 0; } diff --git a/examples/aes_encrypt/aes.c b/examples/aes_encrypt/aes.c index 61848c10f..4b9c99ac1 100644 --- a/examples/aes_encrypt/aes.c +++ b/examples/aes_encrypt/aes.c @@ -5,9 +5,9 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside - * 2016-2017 Hewlett Packard Enterprise Development LP + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside + * 2016-2019 Hewlett Packard Enterprise Development LP * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -54,82 +54,94 @@ * http://csrc.nist.gov/publications/nistpubs/800-38C/SP800-38C_updated-July20_2007.pdf * Source: https://github.com/B-Con/crypto-algorithms *********************************************************************/ - + /*************************** HEADER FILES ***************************/ -#include -#include #include "aes.h" - +#include +#include + #include - + /****************************** MACROS ******************************/ // The least significant byte of the word is rotated to the end. #define KE_ROTWORD(x) (((x) << 8) | ((x) >> 24)) - -#define TRUE 1 + +#define TRUE 1 #define FALSE 0 - + /**************************** DATA TYPES ****************************/ #define AES_128_ROUNDS 10 #define AES_192_ROUNDS 12 #define AES_256_ROUNDS 14 - + /*********************** FUNCTION DECLARATIONS **********************/ -void ccm_prepare_first_ctr_blk(BYTE counter[], const BYTE nonce[], int nonce_len, int payload_len_store_size); -void ccm_prepare_first_format_blk(BYTE buf[], int assoc_len, int payload_len, int payload_len_store_size, int mac_len, const BYTE nonce[], int nonce_len); -void ccm_format_assoc_data(BYTE buf[], int *end_of_buf, const BYTE assoc[], int assoc_len); -void ccm_format_payload_data(BYTE buf[], int *end_of_buf, const BYTE payload[], int payload_len); -void xor_buf(const BYTE in[], BYTE out[], size_t len); -WORD SubWord(WORD word); -void AddRoundKey(BYTE state[][4], const WORD w[]); -void SubBytes(BYTE state[][4]); -void InvSubBytes(BYTE state[][4]); -void ShiftRows(BYTE state[][4]); -void MixColumns(BYTE state[][4]); -void InvMixColumns(BYTE state[][4]); -void InvShiftRows(BYTE state[][4]); - +void +ccm_prepare_first_ctr_blk(BYTE counter[], const BYTE nonce[], int nonce_len, int payload_len_store_size); +void +ccm_prepare_first_format_blk(BYTE buf[], int assoc_len, int payload_len, int payload_len_store_size, int mac_len, + const BYTE nonce[], int nonce_len); +void +ccm_format_assoc_data(BYTE buf[], int *end_of_buf, const BYTE assoc[], int assoc_len); +void +ccm_format_payload_data(BYTE buf[], int *end_of_buf, const BYTE payload[], int payload_len); +void +xor_buf(const BYTE in[], BYTE out[], size_t len); +WORD +SubWord(WORD word); +void +AddRoundKey(BYTE state[][4], const WORD w[]); +void +SubBytes(BYTE state[][4]); +void +InvSubBytes(BYTE state[][4]); +void +ShiftRows(BYTE state[][4]); +void +MixColumns(BYTE state[][4]); +void +InvMixColumns(BYTE state[][4]); +void +InvShiftRows(BYTE state[][4]); + /**************************** VARIABLES *****************************/ // This is the specified AES SBox. To look up a substitution value, put the first // nibble in the first index (row) and the second nibble in the second index (column). static const BYTE aes_sbox[16][16] = { - {0x63,0x7C,0x77,0x7B,0xF2,0x6B,0x6F,0xC5,0x30,0x01,0x67,0x2B,0xFE,0xD7,0xAB,0x76}, - {0xCA,0x82,0xC9,0x7D,0xFA,0x59,0x47,0xF0,0xAD,0xD4,0xA2,0xAF,0x9C,0xA4,0x72,0xC0}, - {0xB7,0xFD,0x93,0x26,0x36,0x3F,0xF7,0xCC,0x34,0xA5,0xE5,0xF1,0x71,0xD8,0x31,0x15}, - {0x04,0xC7,0x23,0xC3,0x18,0x96,0x05,0x9A,0x07,0x12,0x80,0xE2,0xEB,0x27,0xB2,0x75}, - {0x09,0x83,0x2C,0x1A,0x1B,0x6E,0x5A,0xA0,0x52,0x3B,0xD6,0xB3,0x29,0xE3,0x2F,0x84}, - {0x53,0xD1,0x00,0xED,0x20,0xFC,0xB1,0x5B,0x6A,0xCB,0xBE,0x39,0x4A,0x4C,0x58,0xCF}, - {0xD0,0xEF,0xAA,0xFB,0x43,0x4D,0x33,0x85,0x45,0xF9,0x02,0x7F,0x50,0x3C,0x9F,0xA8}, - {0x51,0xA3,0x40,0x8F,0x92,0x9D,0x38,0xF5,0xBC,0xB6,0xDA,0x21,0x10,0xFF,0xF3,0xD2}, - {0xCD,0x0C,0x13,0xEC,0x5F,0x97,0x44,0x17,0xC4,0xA7,0x7E,0x3D,0x64,0x5D,0x19,0x73}, - {0x60,0x81,0x4F,0xDC,0x22,0x2A,0x90,0x88,0x46,0xEE,0xB8,0x14,0xDE,0x5E,0x0B,0xDB}, - {0xE0,0x32,0x3A,0x0A,0x49,0x06,0x24,0x5C,0xC2,0xD3,0xAC,0x62,0x91,0x95,0xE4,0x79}, - {0xE7,0xC8,0x37,0x6D,0x8D,0xD5,0x4E,0xA9,0x6C,0x56,0xF4,0xEA,0x65,0x7A,0xAE,0x08}, - {0xBA,0x78,0x25,0x2E,0x1C,0xA6,0xB4,0xC6,0xE8,0xDD,0x74,0x1F,0x4B,0xBD,0x8B,0x8A}, - {0x70,0x3E,0xB5,0x66,0x48,0x03,0xF6,0x0E,0x61,0x35,0x57,0xB9,0x86,0xC1,0x1D,0x9E}, - {0xE1,0xF8,0x98,0x11,0x69,0xD9,0x8E,0x94,0x9B,0x1E,0x87,0xE9,0xCE,0x55,0x28,0xDF}, - {0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68,0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16} -}; - + {0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76}, + {0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0}, + {0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15}, + {0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75}, + {0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84}, + {0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF}, + {0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8}, + {0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2}, + {0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73}, + {0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB}, + {0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79}, + {0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08}, + {0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A}, + {0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E}, + {0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF}, + {0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16}}; + static const BYTE aes_invsbox[16][16] = { - {0x52,0x09,0x6A,0xD5,0x30,0x36,0xA5,0x38,0xBF,0x40,0xA3,0x9E,0x81,0xF3,0xD7,0xFB}, - {0x7C,0xE3,0x39,0x82,0x9B,0x2F,0xFF,0x87,0x34,0x8E,0x43,0x44,0xC4,0xDE,0xE9,0xCB}, - {0x54,0x7B,0x94,0x32,0xA6,0xC2,0x23,0x3D,0xEE,0x4C,0x95,0x0B,0x42,0xFA,0xC3,0x4E}, - {0x08,0x2E,0xA1,0x66,0x28,0xD9,0x24,0xB2,0x76,0x5B,0xA2,0x49,0x6D,0x8B,0xD1,0x25}, - {0x72,0xF8,0xF6,0x64,0x86,0x68,0x98,0x16,0xD4,0xA4,0x5C,0xCC,0x5D,0x65,0xB6,0x92}, - {0x6C,0x70,0x48,0x50,0xFD,0xED,0xB9,0xDA,0x5E,0x15,0x46,0x57,0xA7,0x8D,0x9D,0x84}, - {0x90,0xD8,0xAB,0x00,0x8C,0xBC,0xD3,0x0A,0xF7,0xE4,0x58,0x05,0xB8,0xB3,0x45,0x06}, - {0xD0,0x2C,0x1E,0x8F,0xCA,0x3F,0x0F,0x02,0xC1,0xAF,0xBD,0x03,0x01,0x13,0x8A,0x6B}, - {0x3A,0x91,0x11,0x41,0x4F,0x67,0xDC,0xEA,0x97,0xF2,0xCF,0xCE,0xF0,0xB4,0xE6,0x73}, - {0x96,0xAC,0x74,0x22,0xE7,0xAD,0x35,0x85,0xE2,0xF9,0x37,0xE8,0x1C,0x75,0xDF,0x6E}, - {0x47,0xF1,0x1A,0x71,0x1D,0x29,0xC5,0x89,0x6F,0xB7,0x62,0x0E,0xAA,0x18,0xBE,0x1B}, - {0xFC,0x56,0x3E,0x4B,0xC6,0xD2,0x79,0x20,0x9A,0xDB,0xC0,0xFE,0x78,0xCD,0x5A,0xF4}, - {0x1F,0xDD,0xA8,0x33,0x88,0x07,0xC7,0x31,0xB1,0x12,0x10,0x59,0x27,0x80,0xEC,0x5F}, - {0x60,0x51,0x7F,0xA9,0x19,0xB5,0x4A,0x0D,0x2D,0xE5,0x7A,0x9F,0x93,0xC9,0x9C,0xEF}, - {0xA0,0xE0,0x3B,0x4D,0xAE,0x2A,0xF5,0xB0,0xC8,0xEB,0xBB,0x3C,0x83,0x53,0x99,0x61}, - {0x17,0x2B,0x04,0x7E,0xBA,0x77,0xD6,0x26,0xE1,0x69,0x14,0x63,0x55,0x21,0x0C,0x7D} -}; - + {0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB}, + {0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB}, + {0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E}, + {0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25}, + {0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92}, + {0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84}, + {0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06}, + {0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B}, + {0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73}, + {0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E}, + {0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B}, + {0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4}, + {0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F}, + {0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF}, + {0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61}, + {0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D}}; + // This table stores pre-calculated values for all possible GF(2^8) calculations.This // table is only used by the (Inv)MixColumns steps. // USAGE: The second index (column) is the coefficient of multiplication. Only 7 different @@ -137,986 +149,1041 @@ static const BYTE aes_invsbox[16][16] = { // 1 is negligible leaving only 6 coefficients. Each column of the table is devoted to one // of these coefficients, in the ascending order of value, from values 0x00 to 0xFF. static const BYTE gf_mul[256][6] = { - {0x00,0x00,0x00,0x00,0x00,0x00},{0x02,0x03,0x09,0x0b,0x0d,0x0e}, - {0x04,0x06,0x12,0x16,0x1a,0x1c},{0x06,0x05,0x1b,0x1d,0x17,0x12}, - {0x08,0x0c,0x24,0x2c,0x34,0x38},{0x0a,0x0f,0x2d,0x27,0x39,0x36}, - {0x0c,0x0a,0x36,0x3a,0x2e,0x24},{0x0e,0x09,0x3f,0x31,0x23,0x2a}, - {0x10,0x18,0x48,0x58,0x68,0x70},{0x12,0x1b,0x41,0x53,0x65,0x7e}, - {0x14,0x1e,0x5a,0x4e,0x72,0x6c},{0x16,0x1d,0x53,0x45,0x7f,0x62}, - {0x18,0x14,0x6c,0x74,0x5c,0x48},{0x1a,0x17,0x65,0x7f,0x51,0x46}, - {0x1c,0x12,0x7e,0x62,0x46,0x54},{0x1e,0x11,0x77,0x69,0x4b,0x5a}, - {0x20,0x30,0x90,0xb0,0xd0,0xe0},{0x22,0x33,0x99,0xbb,0xdd,0xee}, - {0x24,0x36,0x82,0xa6,0xca,0xfc},{0x26,0x35,0x8b,0xad,0xc7,0xf2}, - {0x28,0x3c,0xb4,0x9c,0xe4,0xd8},{0x2a,0x3f,0xbd,0x97,0xe9,0xd6}, - {0x2c,0x3a,0xa6,0x8a,0xfe,0xc4},{0x2e,0x39,0xaf,0x81,0xf3,0xca}, - {0x30,0x28,0xd8,0xe8,0xb8,0x90},{0x32,0x2b,0xd1,0xe3,0xb5,0x9e}, - {0x34,0x2e,0xca,0xfe,0xa2,0x8c},{0x36,0x2d,0xc3,0xf5,0xaf,0x82}, - {0x38,0x24,0xfc,0xc4,0x8c,0xa8},{0x3a,0x27,0xf5,0xcf,0x81,0xa6}, - {0x3c,0x22,0xee,0xd2,0x96,0xb4},{0x3e,0x21,0xe7,0xd9,0x9b,0xba}, - {0x40,0x60,0x3b,0x7b,0xbb,0xdb},{0x42,0x63,0x32,0x70,0xb6,0xd5}, - {0x44,0x66,0x29,0x6d,0xa1,0xc7},{0x46,0x65,0x20,0x66,0xac,0xc9}, - {0x48,0x6c,0x1f,0x57,0x8f,0xe3},{0x4a,0x6f,0x16,0x5c,0x82,0xed}, - {0x4c,0x6a,0x0d,0x41,0x95,0xff},{0x4e,0x69,0x04,0x4a,0x98,0xf1}, - {0x50,0x78,0x73,0x23,0xd3,0xab},{0x52,0x7b,0x7a,0x28,0xde,0xa5}, - {0x54,0x7e,0x61,0x35,0xc9,0xb7},{0x56,0x7d,0x68,0x3e,0xc4,0xb9}, - {0x58,0x74,0x57,0x0f,0xe7,0x93},{0x5a,0x77,0x5e,0x04,0xea,0x9d}, - {0x5c,0x72,0x45,0x19,0xfd,0x8f},{0x5e,0x71,0x4c,0x12,0xf0,0x81}, - {0x60,0x50,0xab,0xcb,0x6b,0x3b},{0x62,0x53,0xa2,0xc0,0x66,0x35}, - {0x64,0x56,0xb9,0xdd,0x71,0x27},{0x66,0x55,0xb0,0xd6,0x7c,0x29}, - {0x68,0x5c,0x8f,0xe7,0x5f,0x03},{0x6a,0x5f,0x86,0xec,0x52,0x0d}, - {0x6c,0x5a,0x9d,0xf1,0x45,0x1f},{0x6e,0x59,0x94,0xfa,0x48,0x11}, - {0x70,0x48,0xe3,0x93,0x03,0x4b},{0x72,0x4b,0xea,0x98,0x0e,0x45}, - {0x74,0x4e,0xf1,0x85,0x19,0x57},{0x76,0x4d,0xf8,0x8e,0x14,0x59}, - {0x78,0x44,0xc7,0xbf,0x37,0x73},{0x7a,0x47,0xce,0xb4,0x3a,0x7d}, - {0x7c,0x42,0xd5,0xa9,0x2d,0x6f},{0x7e,0x41,0xdc,0xa2,0x20,0x61}, - {0x80,0xc0,0x76,0xf6,0x6d,0xad},{0x82,0xc3,0x7f,0xfd,0x60,0xa3}, - {0x84,0xc6,0x64,0xe0,0x77,0xb1},{0x86,0xc5,0x6d,0xeb,0x7a,0xbf}, - {0x88,0xcc,0x52,0xda,0x59,0x95},{0x8a,0xcf,0x5b,0xd1,0x54,0x9b}, - {0x8c,0xca,0x40,0xcc,0x43,0x89},{0x8e,0xc9,0x49,0xc7,0x4e,0x87}, - {0x90,0xd8,0x3e,0xae,0x05,0xdd},{0x92,0xdb,0x37,0xa5,0x08,0xd3}, - {0x94,0xde,0x2c,0xb8,0x1f,0xc1},{0x96,0xdd,0x25,0xb3,0x12,0xcf}, - {0x98,0xd4,0x1a,0x82,0x31,0xe5},{0x9a,0xd7,0x13,0x89,0x3c,0xeb}, - {0x9c,0xd2,0x08,0x94,0x2b,0xf9},{0x9e,0xd1,0x01,0x9f,0x26,0xf7}, - {0xa0,0xf0,0xe6,0x46,0xbd,0x4d},{0xa2,0xf3,0xef,0x4d,0xb0,0x43}, - {0xa4,0xf6,0xf4,0x50,0xa7,0x51},{0xa6,0xf5,0xfd,0x5b,0xaa,0x5f}, - {0xa8,0xfc,0xc2,0x6a,0x89,0x75},{0xaa,0xff,0xcb,0x61,0x84,0x7b}, - {0xac,0xfa,0xd0,0x7c,0x93,0x69},{0xae,0xf9,0xd9,0x77,0x9e,0x67}, - {0xb0,0xe8,0xae,0x1e,0xd5,0x3d},{0xb2,0xeb,0xa7,0x15,0xd8,0x33}, - {0xb4,0xee,0xbc,0x08,0xcf,0x21},{0xb6,0xed,0xb5,0x03,0xc2,0x2f}, - {0xb8,0xe4,0x8a,0x32,0xe1,0x05},{0xba,0xe7,0x83,0x39,0xec,0x0b}, - {0xbc,0xe2,0x98,0x24,0xfb,0x19},{0xbe,0xe1,0x91,0x2f,0xf6,0x17}, - {0xc0,0xa0,0x4d,0x8d,0xd6,0x76},{0xc2,0xa3,0x44,0x86,0xdb,0x78}, - {0xc4,0xa6,0x5f,0x9b,0xcc,0x6a},{0xc6,0xa5,0x56,0x90,0xc1,0x64}, - {0xc8,0xac,0x69,0xa1,0xe2,0x4e},{0xca,0xaf,0x60,0xaa,0xef,0x40}, - {0xcc,0xaa,0x7b,0xb7,0xf8,0x52},{0xce,0xa9,0x72,0xbc,0xf5,0x5c}, - {0xd0,0xb8,0x05,0xd5,0xbe,0x06},{0xd2,0xbb,0x0c,0xde,0xb3,0x08}, - {0xd4,0xbe,0x17,0xc3,0xa4,0x1a},{0xd6,0xbd,0x1e,0xc8,0xa9,0x14}, - {0xd8,0xb4,0x21,0xf9,0x8a,0x3e},{0xda,0xb7,0x28,0xf2,0x87,0x30}, - {0xdc,0xb2,0x33,0xef,0x90,0x22},{0xde,0xb1,0x3a,0xe4,0x9d,0x2c}, - {0xe0,0x90,0xdd,0x3d,0x06,0x96},{0xe2,0x93,0xd4,0x36,0x0b,0x98}, - {0xe4,0x96,0xcf,0x2b,0x1c,0x8a},{0xe6,0x95,0xc6,0x20,0x11,0x84}, - {0xe8,0x9c,0xf9,0x11,0x32,0xae},{0xea,0x9f,0xf0,0x1a,0x3f,0xa0}, - {0xec,0x9a,0xeb,0x07,0x28,0xb2},{0xee,0x99,0xe2,0x0c,0x25,0xbc}, - {0xf0,0x88,0x95,0x65,0x6e,0xe6},{0xf2,0x8b,0x9c,0x6e,0x63,0xe8}, - {0xf4,0x8e,0x87,0x73,0x74,0xfa},{0xf6,0x8d,0x8e,0x78,0x79,0xf4}, - {0xf8,0x84,0xb1,0x49,0x5a,0xde},{0xfa,0x87,0xb8,0x42,0x57,0xd0}, - {0xfc,0x82,0xa3,0x5f,0x40,0xc2},{0xfe,0x81,0xaa,0x54,0x4d,0xcc}, - {0x1b,0x9b,0xec,0xf7,0xda,0x41},{0x19,0x98,0xe5,0xfc,0xd7,0x4f}, - {0x1f,0x9d,0xfe,0xe1,0xc0,0x5d},{0x1d,0x9e,0xf7,0xea,0xcd,0x53}, - {0x13,0x97,0xc8,0xdb,0xee,0x79},{0x11,0x94,0xc1,0xd0,0xe3,0x77}, - {0x17,0x91,0xda,0xcd,0xf4,0x65},{0x15,0x92,0xd3,0xc6,0xf9,0x6b}, - {0x0b,0x83,0xa4,0xaf,0xb2,0x31},{0x09,0x80,0xad,0xa4,0xbf,0x3f}, - {0x0f,0x85,0xb6,0xb9,0xa8,0x2d},{0x0d,0x86,0xbf,0xb2,0xa5,0x23}, - {0x03,0x8f,0x80,0x83,0x86,0x09},{0x01,0x8c,0x89,0x88,0x8b,0x07}, - {0x07,0x89,0x92,0x95,0x9c,0x15},{0x05,0x8a,0x9b,0x9e,0x91,0x1b}, - {0x3b,0xab,0x7c,0x47,0x0a,0xa1},{0x39,0xa8,0x75,0x4c,0x07,0xaf}, - {0x3f,0xad,0x6e,0x51,0x10,0xbd},{0x3d,0xae,0x67,0x5a,0x1d,0xb3}, - {0x33,0xa7,0x58,0x6b,0x3e,0x99},{0x31,0xa4,0x51,0x60,0x33,0x97}, - {0x37,0xa1,0x4a,0x7d,0x24,0x85},{0x35,0xa2,0x43,0x76,0x29,0x8b}, - {0x2b,0xb3,0x34,0x1f,0x62,0xd1},{0x29,0xb0,0x3d,0x14,0x6f,0xdf}, - {0x2f,0xb5,0x26,0x09,0x78,0xcd},{0x2d,0xb6,0x2f,0x02,0x75,0xc3}, - {0x23,0xbf,0x10,0x33,0x56,0xe9},{0x21,0xbc,0x19,0x38,0x5b,0xe7}, - {0x27,0xb9,0x02,0x25,0x4c,0xf5},{0x25,0xba,0x0b,0x2e,0x41,0xfb}, - {0x5b,0xfb,0xd7,0x8c,0x61,0x9a},{0x59,0xf8,0xde,0x87,0x6c,0x94}, - {0x5f,0xfd,0xc5,0x9a,0x7b,0x86},{0x5d,0xfe,0xcc,0x91,0x76,0x88}, - {0x53,0xf7,0xf3,0xa0,0x55,0xa2},{0x51,0xf4,0xfa,0xab,0x58,0xac}, - {0x57,0xf1,0xe1,0xb6,0x4f,0xbe},{0x55,0xf2,0xe8,0xbd,0x42,0xb0}, - {0x4b,0xe3,0x9f,0xd4,0x09,0xea},{0x49,0xe0,0x96,0xdf,0x04,0xe4}, - {0x4f,0xe5,0x8d,0xc2,0x13,0xf6},{0x4d,0xe6,0x84,0xc9,0x1e,0xf8}, - {0x43,0xef,0xbb,0xf8,0x3d,0xd2},{0x41,0xec,0xb2,0xf3,0x30,0xdc}, - {0x47,0xe9,0xa9,0xee,0x27,0xce},{0x45,0xea,0xa0,0xe5,0x2a,0xc0}, - {0x7b,0xcb,0x47,0x3c,0xb1,0x7a},{0x79,0xc8,0x4e,0x37,0xbc,0x74}, - {0x7f,0xcd,0x55,0x2a,0xab,0x66},{0x7d,0xce,0x5c,0x21,0xa6,0x68}, - {0x73,0xc7,0x63,0x10,0x85,0x42},{0x71,0xc4,0x6a,0x1b,0x88,0x4c}, - {0x77,0xc1,0x71,0x06,0x9f,0x5e},{0x75,0xc2,0x78,0x0d,0x92,0x50}, - {0x6b,0xd3,0x0f,0x64,0xd9,0x0a},{0x69,0xd0,0x06,0x6f,0xd4,0x04}, - {0x6f,0xd5,0x1d,0x72,0xc3,0x16},{0x6d,0xd6,0x14,0x79,0xce,0x18}, - {0x63,0xdf,0x2b,0x48,0xed,0x32},{0x61,0xdc,0x22,0x43,0xe0,0x3c}, - {0x67,0xd9,0x39,0x5e,0xf7,0x2e},{0x65,0xda,0x30,0x55,0xfa,0x20}, - {0x9b,0x5b,0x9a,0x01,0xb7,0xec},{0x99,0x58,0x93,0x0a,0xba,0xe2}, - {0x9f,0x5d,0x88,0x17,0xad,0xf0},{0x9d,0x5e,0x81,0x1c,0xa0,0xfe}, - {0x93,0x57,0xbe,0x2d,0x83,0xd4},{0x91,0x54,0xb7,0x26,0x8e,0xda}, - {0x97,0x51,0xac,0x3b,0x99,0xc8},{0x95,0x52,0xa5,0x30,0x94,0xc6}, - {0x8b,0x43,0xd2,0x59,0xdf,0x9c},{0x89,0x40,0xdb,0x52,0xd2,0x92}, - {0x8f,0x45,0xc0,0x4f,0xc5,0x80},{0x8d,0x46,0xc9,0x44,0xc8,0x8e}, - {0x83,0x4f,0xf6,0x75,0xeb,0xa4},{0x81,0x4c,0xff,0x7e,0xe6,0xaa}, - {0x87,0x49,0xe4,0x63,0xf1,0xb8},{0x85,0x4a,0xed,0x68,0xfc,0xb6}, - {0xbb,0x6b,0x0a,0xb1,0x67,0x0c},{0xb9,0x68,0x03,0xba,0x6a,0x02}, - {0xbf,0x6d,0x18,0xa7,0x7d,0x10},{0xbd,0x6e,0x11,0xac,0x70,0x1e}, - {0xb3,0x67,0x2e,0x9d,0x53,0x34},{0xb1,0x64,0x27,0x96,0x5e,0x3a}, - {0xb7,0x61,0x3c,0x8b,0x49,0x28},{0xb5,0x62,0x35,0x80,0x44,0x26}, - {0xab,0x73,0x42,0xe9,0x0f,0x7c},{0xa9,0x70,0x4b,0xe2,0x02,0x72}, - {0xaf,0x75,0x50,0xff,0x15,0x60},{0xad,0x76,0x59,0xf4,0x18,0x6e}, - {0xa3,0x7f,0x66,0xc5,0x3b,0x44},{0xa1,0x7c,0x6f,0xce,0x36,0x4a}, - {0xa7,0x79,0x74,0xd3,0x21,0x58},{0xa5,0x7a,0x7d,0xd8,0x2c,0x56}, - {0xdb,0x3b,0xa1,0x7a,0x0c,0x37},{0xd9,0x38,0xa8,0x71,0x01,0x39}, - {0xdf,0x3d,0xb3,0x6c,0x16,0x2b},{0xdd,0x3e,0xba,0x67,0x1b,0x25}, - {0xd3,0x37,0x85,0x56,0x38,0x0f},{0xd1,0x34,0x8c,0x5d,0x35,0x01}, - {0xd7,0x31,0x97,0x40,0x22,0x13},{0xd5,0x32,0x9e,0x4b,0x2f,0x1d}, - {0xcb,0x23,0xe9,0x22,0x64,0x47},{0xc9,0x20,0xe0,0x29,0x69,0x49}, - {0xcf,0x25,0xfb,0x34,0x7e,0x5b},{0xcd,0x26,0xf2,0x3f,0x73,0x55}, - {0xc3,0x2f,0xcd,0x0e,0x50,0x7f},{0xc1,0x2c,0xc4,0x05,0x5d,0x71}, - {0xc7,0x29,0xdf,0x18,0x4a,0x63},{0xc5,0x2a,0xd6,0x13,0x47,0x6d}, - {0xfb,0x0b,0x31,0xca,0xdc,0xd7},{0xf9,0x08,0x38,0xc1,0xd1,0xd9}, - {0xff,0x0d,0x23,0xdc,0xc6,0xcb},{0xfd,0x0e,0x2a,0xd7,0xcb,0xc5}, - {0xf3,0x07,0x15,0xe6,0xe8,0xef},{0xf1,0x04,0x1c,0xed,0xe5,0xe1}, - {0xf7,0x01,0x07,0xf0,0xf2,0xf3},{0xf5,0x02,0x0e,0xfb,0xff,0xfd}, - {0xeb,0x13,0x79,0x92,0xb4,0xa7},{0xe9,0x10,0x70,0x99,0xb9,0xa9}, - {0xef,0x15,0x6b,0x84,0xae,0xbb},{0xed,0x16,0x62,0x8f,0xa3,0xb5}, - {0xe3,0x1f,0x5d,0xbe,0x80,0x9f},{0xe1,0x1c,0x54,0xb5,0x8d,0x91}, - {0xe7,0x19,0x4f,0xa8,0x9a,0x83},{0xe5,0x1a,0x46,0xa3,0x97,0x8d} -}; - + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x02, 0x03, 0x09, 0x0b, 0x0d, 0x0e}, {0x04, 0x06, 0x12, 0x16, 0x1a, 0x1c}, + {0x06, 0x05, 0x1b, 0x1d, 0x17, 0x12}, {0x08, 0x0c, 0x24, 0x2c, 0x34, 0x38}, {0x0a, 0x0f, 0x2d, 0x27, 0x39, 0x36}, + {0x0c, 0x0a, 0x36, 0x3a, 0x2e, 0x24}, {0x0e, 0x09, 0x3f, 0x31, 0x23, 0x2a}, {0x10, 0x18, 0x48, 0x58, 0x68, 0x70}, + {0x12, 0x1b, 0x41, 0x53, 0x65, 0x7e}, {0x14, 0x1e, 0x5a, 0x4e, 0x72, 0x6c}, {0x16, 0x1d, 0x53, 0x45, 0x7f, 0x62}, + {0x18, 0x14, 0x6c, 0x74, 0x5c, 0x48}, {0x1a, 0x17, 0x65, 0x7f, 0x51, 0x46}, {0x1c, 0x12, 0x7e, 0x62, 0x46, 0x54}, + {0x1e, 0x11, 0x77, 0x69, 0x4b, 0x5a}, {0x20, 0x30, 0x90, 0xb0, 0xd0, 0xe0}, {0x22, 0x33, 0x99, 0xbb, 0xdd, 0xee}, + {0x24, 0x36, 0x82, 0xa6, 0xca, 0xfc}, {0x26, 0x35, 0x8b, 0xad, 0xc7, 0xf2}, {0x28, 0x3c, 0xb4, 0x9c, 0xe4, 0xd8}, + {0x2a, 0x3f, 0xbd, 0x97, 0xe9, 0xd6}, {0x2c, 0x3a, 0xa6, 0x8a, 0xfe, 0xc4}, {0x2e, 0x39, 0xaf, 0x81, 0xf3, 0xca}, + {0x30, 0x28, 0xd8, 0xe8, 0xb8, 0x90}, {0x32, 0x2b, 0xd1, 0xe3, 0xb5, 0x9e}, {0x34, 0x2e, 0xca, 0xfe, 0xa2, 0x8c}, + {0x36, 0x2d, 0xc3, 0xf5, 0xaf, 0x82}, {0x38, 0x24, 0xfc, 0xc4, 0x8c, 0xa8}, {0x3a, 0x27, 0xf5, 0xcf, 0x81, 0xa6}, + {0x3c, 0x22, 0xee, 0xd2, 0x96, 0xb4}, {0x3e, 0x21, 0xe7, 0xd9, 0x9b, 0xba}, {0x40, 0x60, 0x3b, 0x7b, 0xbb, 0xdb}, + {0x42, 0x63, 0x32, 0x70, 0xb6, 0xd5}, {0x44, 0x66, 0x29, 0x6d, 0xa1, 0xc7}, {0x46, 0x65, 0x20, 0x66, 0xac, 0xc9}, + {0x48, 0x6c, 0x1f, 0x57, 0x8f, 0xe3}, {0x4a, 0x6f, 0x16, 0x5c, 0x82, 0xed}, {0x4c, 0x6a, 0x0d, 0x41, 0x95, 0xff}, + {0x4e, 0x69, 0x04, 0x4a, 0x98, 0xf1}, {0x50, 0x78, 0x73, 0x23, 0xd3, 0xab}, {0x52, 0x7b, 0x7a, 0x28, 0xde, 0xa5}, + {0x54, 0x7e, 0x61, 0x35, 0xc9, 0xb7}, {0x56, 0x7d, 0x68, 0x3e, 0xc4, 0xb9}, {0x58, 0x74, 0x57, 0x0f, 0xe7, 0x93}, + {0x5a, 0x77, 0x5e, 0x04, 0xea, 0x9d}, {0x5c, 0x72, 0x45, 0x19, 0xfd, 0x8f}, {0x5e, 0x71, 0x4c, 0x12, 0xf0, 0x81}, + {0x60, 0x50, 0xab, 0xcb, 0x6b, 0x3b}, {0x62, 0x53, 0xa2, 0xc0, 0x66, 0x35}, {0x64, 0x56, 0xb9, 0xdd, 0x71, 0x27}, + {0x66, 0x55, 0xb0, 0xd6, 0x7c, 0x29}, {0x68, 0x5c, 0x8f, 0xe7, 0x5f, 0x03}, {0x6a, 0x5f, 0x86, 0xec, 0x52, 0x0d}, + {0x6c, 0x5a, 0x9d, 0xf1, 0x45, 0x1f}, {0x6e, 0x59, 0x94, 0xfa, 0x48, 0x11}, {0x70, 0x48, 0xe3, 0x93, 0x03, 0x4b}, + {0x72, 0x4b, 0xea, 0x98, 0x0e, 0x45}, {0x74, 0x4e, 0xf1, 0x85, 0x19, 0x57}, {0x76, 0x4d, 0xf8, 0x8e, 0x14, 0x59}, + {0x78, 0x44, 0xc7, 0xbf, 0x37, 0x73}, {0x7a, 0x47, 0xce, 0xb4, 0x3a, 0x7d}, {0x7c, 0x42, 0xd5, 0xa9, 0x2d, 0x6f}, + {0x7e, 0x41, 0xdc, 0xa2, 0x20, 0x61}, {0x80, 0xc0, 0x76, 0xf6, 0x6d, 0xad}, {0x82, 0xc3, 0x7f, 0xfd, 0x60, 0xa3}, + {0x84, 0xc6, 0x64, 0xe0, 0x77, 0xb1}, {0x86, 0xc5, 0x6d, 0xeb, 0x7a, 0xbf}, {0x88, 0xcc, 0x52, 0xda, 0x59, 0x95}, + {0x8a, 0xcf, 0x5b, 0xd1, 0x54, 0x9b}, {0x8c, 0xca, 0x40, 0xcc, 0x43, 0x89}, {0x8e, 0xc9, 0x49, 0xc7, 0x4e, 0x87}, + {0x90, 0xd8, 0x3e, 0xae, 0x05, 0xdd}, {0x92, 0xdb, 0x37, 0xa5, 0x08, 0xd3}, {0x94, 0xde, 0x2c, 0xb8, 0x1f, 0xc1}, + {0x96, 0xdd, 0x25, 0xb3, 0x12, 0xcf}, {0x98, 0xd4, 0x1a, 0x82, 0x31, 0xe5}, {0x9a, 0xd7, 0x13, 0x89, 0x3c, 0xeb}, + {0x9c, 0xd2, 0x08, 0x94, 0x2b, 0xf9}, {0x9e, 0xd1, 0x01, 0x9f, 0x26, 0xf7}, {0xa0, 0xf0, 0xe6, 0x46, 0xbd, 0x4d}, + {0xa2, 0xf3, 0xef, 0x4d, 0xb0, 0x43}, {0xa4, 0xf6, 0xf4, 0x50, 0xa7, 0x51}, {0xa6, 0xf5, 0xfd, 0x5b, 0xaa, 0x5f}, + {0xa8, 0xfc, 0xc2, 0x6a, 0x89, 0x75}, {0xaa, 0xff, 0xcb, 0x61, 0x84, 0x7b}, {0xac, 0xfa, 0xd0, 0x7c, 0x93, 0x69}, + {0xae, 0xf9, 0xd9, 0x77, 0x9e, 0x67}, {0xb0, 0xe8, 0xae, 0x1e, 0xd5, 0x3d}, {0xb2, 0xeb, 0xa7, 0x15, 0xd8, 0x33}, + {0xb4, 0xee, 0xbc, 0x08, 0xcf, 0x21}, {0xb6, 0xed, 0xb5, 0x03, 0xc2, 0x2f}, {0xb8, 0xe4, 0x8a, 0x32, 0xe1, 0x05}, + {0xba, 0xe7, 0x83, 0x39, 0xec, 0x0b}, {0xbc, 0xe2, 0x98, 0x24, 0xfb, 0x19}, {0xbe, 0xe1, 0x91, 0x2f, 0xf6, 0x17}, + {0xc0, 0xa0, 0x4d, 0x8d, 0xd6, 0x76}, {0xc2, 0xa3, 0x44, 0x86, 0xdb, 0x78}, {0xc4, 0xa6, 0x5f, 0x9b, 0xcc, 0x6a}, + {0xc6, 0xa5, 0x56, 0x90, 0xc1, 0x64}, {0xc8, 0xac, 0x69, 0xa1, 0xe2, 0x4e}, {0xca, 0xaf, 0x60, 0xaa, 0xef, 0x40}, + {0xcc, 0xaa, 0x7b, 0xb7, 0xf8, 0x52}, {0xce, 0xa9, 0x72, 0xbc, 0xf5, 0x5c}, {0xd0, 0xb8, 0x05, 0xd5, 0xbe, 0x06}, + {0xd2, 0xbb, 0x0c, 0xde, 0xb3, 0x08}, {0xd4, 0xbe, 0x17, 0xc3, 0xa4, 0x1a}, {0xd6, 0xbd, 0x1e, 0xc8, 0xa9, 0x14}, + {0xd8, 0xb4, 0x21, 0xf9, 0x8a, 0x3e}, {0xda, 0xb7, 0x28, 0xf2, 0x87, 0x30}, {0xdc, 0xb2, 0x33, 0xef, 0x90, 0x22}, + {0xde, 0xb1, 0x3a, 0xe4, 0x9d, 0x2c}, {0xe0, 0x90, 0xdd, 0x3d, 0x06, 0x96}, {0xe2, 0x93, 0xd4, 0x36, 0x0b, 0x98}, + {0xe4, 0x96, 0xcf, 0x2b, 0x1c, 0x8a}, {0xe6, 0x95, 0xc6, 0x20, 0x11, 0x84}, {0xe8, 0x9c, 0xf9, 0x11, 0x32, 0xae}, + {0xea, 0x9f, 0xf0, 0x1a, 0x3f, 0xa0}, {0xec, 0x9a, 0xeb, 0x07, 0x28, 0xb2}, {0xee, 0x99, 0xe2, 0x0c, 0x25, 0xbc}, + {0xf0, 0x88, 0x95, 0x65, 0x6e, 0xe6}, {0xf2, 0x8b, 0x9c, 0x6e, 0x63, 0xe8}, {0xf4, 0x8e, 0x87, 0x73, 0x74, 0xfa}, + {0xf6, 0x8d, 0x8e, 0x78, 0x79, 0xf4}, {0xf8, 0x84, 0xb1, 0x49, 0x5a, 0xde}, {0xfa, 0x87, 0xb8, 0x42, 0x57, 0xd0}, + {0xfc, 0x82, 0xa3, 0x5f, 0x40, 0xc2}, {0xfe, 0x81, 0xaa, 0x54, 0x4d, 0xcc}, {0x1b, 0x9b, 0xec, 0xf7, 0xda, 0x41}, + {0x19, 0x98, 0xe5, 0xfc, 0xd7, 0x4f}, {0x1f, 0x9d, 0xfe, 0xe1, 0xc0, 0x5d}, {0x1d, 0x9e, 0xf7, 0xea, 0xcd, 0x53}, + {0x13, 0x97, 0xc8, 0xdb, 0xee, 0x79}, {0x11, 0x94, 0xc1, 0xd0, 0xe3, 0x77}, {0x17, 0x91, 0xda, 0xcd, 0xf4, 0x65}, + {0x15, 0x92, 0xd3, 0xc6, 0xf9, 0x6b}, {0x0b, 0x83, 0xa4, 0xaf, 0xb2, 0x31}, {0x09, 0x80, 0xad, 0xa4, 0xbf, 0x3f}, + {0x0f, 0x85, 0xb6, 0xb9, 0xa8, 0x2d}, {0x0d, 0x86, 0xbf, 0xb2, 0xa5, 0x23}, {0x03, 0x8f, 0x80, 0x83, 0x86, 0x09}, + {0x01, 0x8c, 0x89, 0x88, 0x8b, 0x07}, {0x07, 0x89, 0x92, 0x95, 0x9c, 0x15}, {0x05, 0x8a, 0x9b, 0x9e, 0x91, 0x1b}, + {0x3b, 0xab, 0x7c, 0x47, 0x0a, 0xa1}, {0x39, 0xa8, 0x75, 0x4c, 0x07, 0xaf}, {0x3f, 0xad, 0x6e, 0x51, 0x10, 0xbd}, + {0x3d, 0xae, 0x67, 0x5a, 0x1d, 0xb3}, {0x33, 0xa7, 0x58, 0x6b, 0x3e, 0x99}, {0x31, 0xa4, 0x51, 0x60, 0x33, 0x97}, + {0x37, 0xa1, 0x4a, 0x7d, 0x24, 0x85}, {0x35, 0xa2, 0x43, 0x76, 0x29, 0x8b}, {0x2b, 0xb3, 0x34, 0x1f, 0x62, 0xd1}, + {0x29, 0xb0, 0x3d, 0x14, 0x6f, 0xdf}, {0x2f, 0xb5, 0x26, 0x09, 0x78, 0xcd}, {0x2d, 0xb6, 0x2f, 0x02, 0x75, 0xc3}, + {0x23, 0xbf, 0x10, 0x33, 0x56, 0xe9}, {0x21, 0xbc, 0x19, 0x38, 0x5b, 0xe7}, {0x27, 0xb9, 0x02, 0x25, 0x4c, 0xf5}, + {0x25, 0xba, 0x0b, 0x2e, 0x41, 0xfb}, {0x5b, 0xfb, 0xd7, 0x8c, 0x61, 0x9a}, {0x59, 0xf8, 0xde, 0x87, 0x6c, 0x94}, + {0x5f, 0xfd, 0xc5, 0x9a, 0x7b, 0x86}, {0x5d, 0xfe, 0xcc, 0x91, 0x76, 0x88}, {0x53, 0xf7, 0xf3, 0xa0, 0x55, 0xa2}, + {0x51, 0xf4, 0xfa, 0xab, 0x58, 0xac}, {0x57, 0xf1, 0xe1, 0xb6, 0x4f, 0xbe}, {0x55, 0xf2, 0xe8, 0xbd, 0x42, 0xb0}, + {0x4b, 0xe3, 0x9f, 0xd4, 0x09, 0xea}, {0x49, 0xe0, 0x96, 0xdf, 0x04, 0xe4}, {0x4f, 0xe5, 0x8d, 0xc2, 0x13, 0xf6}, + {0x4d, 0xe6, 0x84, 0xc9, 0x1e, 0xf8}, {0x43, 0xef, 0xbb, 0xf8, 0x3d, 0xd2}, {0x41, 0xec, 0xb2, 0xf3, 0x30, 0xdc}, + {0x47, 0xe9, 0xa9, 0xee, 0x27, 0xce}, {0x45, 0xea, 0xa0, 0xe5, 0x2a, 0xc0}, {0x7b, 0xcb, 0x47, 0x3c, 0xb1, 0x7a}, + {0x79, 0xc8, 0x4e, 0x37, 0xbc, 0x74}, {0x7f, 0xcd, 0x55, 0x2a, 0xab, 0x66}, {0x7d, 0xce, 0x5c, 0x21, 0xa6, 0x68}, + {0x73, 0xc7, 0x63, 0x10, 0x85, 0x42}, {0x71, 0xc4, 0x6a, 0x1b, 0x88, 0x4c}, {0x77, 0xc1, 0x71, 0x06, 0x9f, 0x5e}, + {0x75, 0xc2, 0x78, 0x0d, 0x92, 0x50}, {0x6b, 0xd3, 0x0f, 0x64, 0xd9, 0x0a}, {0x69, 0xd0, 0x06, 0x6f, 0xd4, 0x04}, + {0x6f, 0xd5, 0x1d, 0x72, 0xc3, 0x16}, {0x6d, 0xd6, 0x14, 0x79, 0xce, 0x18}, {0x63, 0xdf, 0x2b, 0x48, 0xed, 0x32}, + {0x61, 0xdc, 0x22, 0x43, 0xe0, 0x3c}, {0x67, 0xd9, 0x39, 0x5e, 0xf7, 0x2e}, {0x65, 0xda, 0x30, 0x55, 0xfa, 0x20}, + {0x9b, 0x5b, 0x9a, 0x01, 0xb7, 0xec}, {0x99, 0x58, 0x93, 0x0a, 0xba, 0xe2}, {0x9f, 0x5d, 0x88, 0x17, 0xad, 0xf0}, + {0x9d, 0x5e, 0x81, 0x1c, 0xa0, 0xfe}, {0x93, 0x57, 0xbe, 0x2d, 0x83, 0xd4}, {0x91, 0x54, 0xb7, 0x26, 0x8e, 0xda}, + {0x97, 0x51, 0xac, 0x3b, 0x99, 0xc8}, {0x95, 0x52, 0xa5, 0x30, 0x94, 0xc6}, {0x8b, 0x43, 0xd2, 0x59, 0xdf, 0x9c}, + {0x89, 0x40, 0xdb, 0x52, 0xd2, 0x92}, {0x8f, 0x45, 0xc0, 0x4f, 0xc5, 0x80}, {0x8d, 0x46, 0xc9, 0x44, 0xc8, 0x8e}, + {0x83, 0x4f, 0xf6, 0x75, 0xeb, 0xa4}, {0x81, 0x4c, 0xff, 0x7e, 0xe6, 0xaa}, {0x87, 0x49, 0xe4, 0x63, 0xf1, 0xb8}, + {0x85, 0x4a, 0xed, 0x68, 0xfc, 0xb6}, {0xbb, 0x6b, 0x0a, 0xb1, 0x67, 0x0c}, {0xb9, 0x68, 0x03, 0xba, 0x6a, 0x02}, + {0xbf, 0x6d, 0x18, 0xa7, 0x7d, 0x10}, {0xbd, 0x6e, 0x11, 0xac, 0x70, 0x1e}, {0xb3, 0x67, 0x2e, 0x9d, 0x53, 0x34}, + {0xb1, 0x64, 0x27, 0x96, 0x5e, 0x3a}, {0xb7, 0x61, 0x3c, 0x8b, 0x49, 0x28}, {0xb5, 0x62, 0x35, 0x80, 0x44, 0x26}, + {0xab, 0x73, 0x42, 0xe9, 0x0f, 0x7c}, {0xa9, 0x70, 0x4b, 0xe2, 0x02, 0x72}, {0xaf, 0x75, 0x50, 0xff, 0x15, 0x60}, + {0xad, 0x76, 0x59, 0xf4, 0x18, 0x6e}, {0xa3, 0x7f, 0x66, 0xc5, 0x3b, 0x44}, {0xa1, 0x7c, 0x6f, 0xce, 0x36, 0x4a}, + {0xa7, 0x79, 0x74, 0xd3, 0x21, 0x58}, {0xa5, 0x7a, 0x7d, 0xd8, 0x2c, 0x56}, {0xdb, 0x3b, 0xa1, 0x7a, 0x0c, 0x37}, + {0xd9, 0x38, 0xa8, 0x71, 0x01, 0x39}, {0xdf, 0x3d, 0xb3, 0x6c, 0x16, 0x2b}, {0xdd, 0x3e, 0xba, 0x67, 0x1b, 0x25}, + {0xd3, 0x37, 0x85, 0x56, 0x38, 0x0f}, {0xd1, 0x34, 0x8c, 0x5d, 0x35, 0x01}, {0xd7, 0x31, 0x97, 0x40, 0x22, 0x13}, + {0xd5, 0x32, 0x9e, 0x4b, 0x2f, 0x1d}, {0xcb, 0x23, 0xe9, 0x22, 0x64, 0x47}, {0xc9, 0x20, 0xe0, 0x29, 0x69, 0x49}, + {0xcf, 0x25, 0xfb, 0x34, 0x7e, 0x5b}, {0xcd, 0x26, 0xf2, 0x3f, 0x73, 0x55}, {0xc3, 0x2f, 0xcd, 0x0e, 0x50, 0x7f}, + {0xc1, 0x2c, 0xc4, 0x05, 0x5d, 0x71}, {0xc7, 0x29, 0xdf, 0x18, 0x4a, 0x63}, {0xc5, 0x2a, 0xd6, 0x13, 0x47, 0x6d}, + {0xfb, 0x0b, 0x31, 0xca, 0xdc, 0xd7}, {0xf9, 0x08, 0x38, 0xc1, 0xd1, 0xd9}, {0xff, 0x0d, 0x23, 0xdc, 0xc6, 0xcb}, + {0xfd, 0x0e, 0x2a, 0xd7, 0xcb, 0xc5}, {0xf3, 0x07, 0x15, 0xe6, 0xe8, 0xef}, {0xf1, 0x04, 0x1c, 0xed, 0xe5, 0xe1}, + {0xf7, 0x01, 0x07, 0xf0, 0xf2, 0xf3}, {0xf5, 0x02, 0x0e, 0xfb, 0xff, 0xfd}, {0xeb, 0x13, 0x79, 0x92, 0xb4, 0xa7}, + {0xe9, 0x10, 0x70, 0x99, 0xb9, 0xa9}, {0xef, 0x15, 0x6b, 0x84, 0xae, 0xbb}, {0xed, 0x16, 0x62, 0x8f, 0xa3, 0xb5}, + {0xe3, 0x1f, 0x5d, 0xbe, 0x80, 0x9f}, {0xe1, 0x1c, 0x54, 0xb5, 0x8d, 0x91}, {0xe7, 0x19, 0x4f, 0xa8, 0x9a, 0x83}, + {0xe5, 0x1a, 0x46, 0xa3, 0x97, 0x8d}}; + /*********************** FUNCTION DEFINITIONS ***********************/ // XORs the in and out buffers, storing the result in out. Length is in bytes. -void xor_buf(const BYTE in[], BYTE out[], size_t len) -{ - size_t idx; - - for (idx = 0; idx < len; idx++) - out[idx] ^= in[idx]; +void +xor_buf(const BYTE in[], BYTE out[], size_t len) { + size_t idx; + + for (idx = 0; idx < len; idx++) + out[idx] ^= in[idx]; } - + /******************* * AES - CBC *******************/ -int aes_encrypt_cbc(const BYTE in[], size_t in_len, BYTE out[], const WORD key[], int keysize, const BYTE iv[]) -{ - BYTE buf_in[AES_BLOCK_SIZE], buf_out[AES_BLOCK_SIZE], iv_buf[AES_BLOCK_SIZE]; - int blocks, idx; - - if (in_len % AES_BLOCK_SIZE != 0) - return(FALSE); - - blocks = in_len / AES_BLOCK_SIZE; - - memcpy(iv_buf, iv, AES_BLOCK_SIZE); - - for (idx = 0; idx < blocks; idx++) { - memcpy(buf_in, &in[idx * AES_BLOCK_SIZE], AES_BLOCK_SIZE); - xor_buf(iv_buf, buf_in, AES_BLOCK_SIZE); - aes_encrypt(buf_in, buf_out, key, keysize); - memcpy(&out[idx * AES_BLOCK_SIZE], buf_out, AES_BLOCK_SIZE); - memcpy(iv_buf, buf_out, AES_BLOCK_SIZE); - } - - return(TRUE); +int +aes_encrypt_cbc(const BYTE in[], size_t in_len, BYTE out[], const WORD key[], int keysize, const BYTE iv[]) { + BYTE buf_in[AES_BLOCK_SIZE], buf_out[AES_BLOCK_SIZE], iv_buf[AES_BLOCK_SIZE]; + int blocks, idx; + + if (in_len % AES_BLOCK_SIZE != 0) + return (FALSE); + + blocks = in_len / AES_BLOCK_SIZE; + + memcpy(iv_buf, iv, AES_BLOCK_SIZE); + + for (idx = 0; idx < blocks; idx++) { + memcpy(buf_in, &in[idx * AES_BLOCK_SIZE], AES_BLOCK_SIZE); + xor_buf(iv_buf, buf_in, AES_BLOCK_SIZE); + aes_encrypt(buf_in, buf_out, key, keysize); + memcpy(&out[idx * AES_BLOCK_SIZE], buf_out, AES_BLOCK_SIZE); + memcpy(iv_buf, buf_out, AES_BLOCK_SIZE); + } + + return (TRUE); } - -int aes_encrypt_cbc_mac(const BYTE in[], size_t in_len, BYTE out[], const WORD key[], int keysize, const BYTE iv[]) -{ - BYTE buf_in[AES_BLOCK_SIZE], buf_out[AES_BLOCK_SIZE], iv_buf[AES_BLOCK_SIZE]; - int blocks, idx; - - if (in_len % AES_BLOCK_SIZE != 0) - return(FALSE); - - blocks = in_len / AES_BLOCK_SIZE; - - memcpy(iv_buf, iv, AES_BLOCK_SIZE); - - for (idx = 0; idx < blocks; idx++) { - memcpy(buf_in, &in[idx * AES_BLOCK_SIZE], AES_BLOCK_SIZE); - xor_buf(iv_buf, buf_in, AES_BLOCK_SIZE); - aes_encrypt(buf_in, buf_out, key, keysize); - memcpy(iv_buf, buf_out, AES_BLOCK_SIZE); - // Do not output all encrypted blocks. - } - - memcpy(out, buf_out, AES_BLOCK_SIZE); // Only output the last block. - - return(TRUE); + +int +aes_encrypt_cbc_mac(const BYTE in[], size_t in_len, BYTE out[], const WORD key[], int keysize, const BYTE iv[]) { + BYTE buf_in[AES_BLOCK_SIZE], buf_out[AES_BLOCK_SIZE], iv_buf[AES_BLOCK_SIZE]; + int blocks, idx; + + if (in_len % AES_BLOCK_SIZE != 0) + return (FALSE); + + blocks = in_len / AES_BLOCK_SIZE; + + memcpy(iv_buf, iv, AES_BLOCK_SIZE); + + for (idx = 0; idx < blocks; idx++) { + memcpy(buf_in, &in[idx * AES_BLOCK_SIZE], AES_BLOCK_SIZE); + xor_buf(iv_buf, buf_in, AES_BLOCK_SIZE); + aes_encrypt(buf_in, buf_out, key, keysize); + memcpy(iv_buf, buf_out, AES_BLOCK_SIZE); + // Do not output all encrypted blocks. + } + + memcpy(out, buf_out, AES_BLOCK_SIZE); // Only output the last block. + + return (TRUE); } - -int aes_decrypt_cbc(const BYTE in[], size_t in_len, BYTE out[], const WORD key[], int keysize, const BYTE iv[]) -{ - BYTE buf_in[AES_BLOCK_SIZE], buf_out[AES_BLOCK_SIZE], iv_buf[AES_BLOCK_SIZE]; - int blocks, idx; - - if (in_len % AES_BLOCK_SIZE != 0) - return(FALSE); - - blocks = in_len / AES_BLOCK_SIZE; - - memcpy(iv_buf, iv, AES_BLOCK_SIZE); - - for (idx = 0; idx < blocks; idx++) { - memcpy(buf_in, &in[idx * AES_BLOCK_SIZE], AES_BLOCK_SIZE); - aes_decrypt(buf_in, buf_out, key, keysize); - xor_buf(iv_buf, buf_out, AES_BLOCK_SIZE); - memcpy(&out[idx * AES_BLOCK_SIZE], buf_out, AES_BLOCK_SIZE); - memcpy(iv_buf, buf_in, AES_BLOCK_SIZE); - } - - return(TRUE); + +int +aes_decrypt_cbc(const BYTE in[], size_t in_len, BYTE out[], const WORD key[], int keysize, const BYTE iv[]) { + BYTE buf_in[AES_BLOCK_SIZE], buf_out[AES_BLOCK_SIZE], iv_buf[AES_BLOCK_SIZE]; + int blocks, idx; + + if (in_len % AES_BLOCK_SIZE != 0) + return (FALSE); + + blocks = in_len / AES_BLOCK_SIZE; + + memcpy(iv_buf, iv, AES_BLOCK_SIZE); + + for (idx = 0; idx < blocks; idx++) { + memcpy(buf_in, &in[idx * AES_BLOCK_SIZE], AES_BLOCK_SIZE); + aes_decrypt(buf_in, buf_out, key, keysize); + xor_buf(iv_buf, buf_out, AES_BLOCK_SIZE); + memcpy(&out[idx * AES_BLOCK_SIZE], buf_out, AES_BLOCK_SIZE); + memcpy(iv_buf, buf_in, AES_BLOCK_SIZE); + } + + return (TRUE); } - + /******************* * AES - CTR *******************/ -void increment_iv(BYTE iv[], int counter_size) -{ - int idx; - - // Use counter_size bytes at the end of the IV as the big-endian integer to increment. - for (idx = AES_BLOCK_SIZE - 1; idx >= AES_BLOCK_SIZE - counter_size; idx--) { - iv[idx]++; - if (iv[idx] != 0 || idx == AES_BLOCK_SIZE - counter_size) - break; - } +void +increment_iv(BYTE iv[], int counter_size) { + int idx; + + // Use counter_size bytes at the end of the IV as the big-endian integer to increment. + for (idx = AES_BLOCK_SIZE - 1; idx >= AES_BLOCK_SIZE - counter_size; idx--) { + iv[idx]++; + if (iv[idx] != 0 || idx == AES_BLOCK_SIZE - counter_size) + break; + } } - + // Performs the encryption in-place, the input and output buffers may be the same. // Input may be an arbitrary length (in bytes). -void aes_encrypt_ctr(const BYTE in[], size_t in_len, BYTE out[], const WORD key[], int keysize, const BYTE iv[]) -{ - size_t idx = 0, last_block_length; - BYTE iv_buf[AES_BLOCK_SIZE], out_buf[AES_BLOCK_SIZE]; - - if (in != out) - memcpy(out, in, in_len); - - memcpy(iv_buf, iv, AES_BLOCK_SIZE); - last_block_length = in_len - AES_BLOCK_SIZE; - - if (in_len > AES_BLOCK_SIZE) { - for (idx = 0; idx < last_block_length; idx += AES_BLOCK_SIZE) { - aes_encrypt(iv_buf, out_buf, key, keysize); - xor_buf(out_buf, &out[idx], AES_BLOCK_SIZE); - increment_iv(iv_buf, AES_BLOCK_SIZE); +void +aes_encrypt_ctr(const BYTE in[], size_t in_len, BYTE out[], const WORD key[], int keysize, const BYTE iv[]) { + size_t idx = 0, last_block_length; + BYTE iv_buf[AES_BLOCK_SIZE], out_buf[AES_BLOCK_SIZE]; + + if (in != out) + memcpy(out, in, in_len); + + memcpy(iv_buf, iv, AES_BLOCK_SIZE); + last_block_length = in_len - AES_BLOCK_SIZE; + + if (in_len > AES_BLOCK_SIZE) { + for (idx = 0; idx < last_block_length; idx += AES_BLOCK_SIZE) { + aes_encrypt(iv_buf, out_buf, key, keysize); + xor_buf(out_buf, &out[idx], AES_BLOCK_SIZE); + increment_iv(iv_buf, AES_BLOCK_SIZE); + } } - } - - aes_encrypt(iv_buf, out_buf, key, keysize); - xor_buf(out_buf, &out[idx], in_len - idx); // Use the Most Significant bytes. + + aes_encrypt(iv_buf, out_buf, key, keysize); + xor_buf(out_buf, &out[idx], in_len - idx); // Use the Most Significant bytes. } - -void aes_decrypt_ctr(const BYTE in[], size_t in_len, BYTE out[], const WORD key[], int keysize, const BYTE iv[]) -{ - // CTR encryption is its own inverse function. - aes_encrypt_ctr(in, in_len, out, key, keysize, iv); + +void +aes_decrypt_ctr(const BYTE in[], size_t in_len, BYTE out[], const WORD key[], int keysize, const BYTE iv[]) { + // CTR encryption is its own inverse function. + aes_encrypt_ctr(in, in_len, out, key, keysize, iv); } - + /******************* * AES - CCM *******************/ // out_len = payload_len + assoc_len -int aes_encrypt_ccm(const BYTE payload[], WORD payload_len, const BYTE assoc[], unsigned short assoc_len, - const BYTE nonce[], unsigned short nonce_len, BYTE out[], WORD *out_len, - WORD mac_len, const BYTE key_str[], int keysize) -{ - BYTE temp_iv[AES_BLOCK_SIZE], counter[AES_BLOCK_SIZE], mac[16], *buf; - int end_of_buf, payload_len_store_size; - WORD key[60]; - - if (mac_len != 4 && mac_len != 6 && mac_len != 8 && mac_len != 10 && - mac_len != 12 && mac_len != 14 && mac_len != 16) - return(FALSE); - - if (nonce_len < 7 || nonce_len > 13) - return(FALSE); - - if (assoc_len > 32768 /* = 2^15 */) - return(FALSE); - - buf = (BYTE*)malloc(payload_len + assoc_len + 48 /*Round both payload and associated data up a block size and add an extra block.*/); - if (! buf) - return(FALSE); - - // Prepare the key for usage. - aes_key_setup(key_str, key, keysize); - - // Format the first block of the formatted data. - payload_len_store_size = AES_BLOCK_SIZE - 1 - nonce_len; - ccm_prepare_first_format_blk(buf, assoc_len, payload_len, payload_len_store_size, mac_len, nonce, nonce_len); - end_of_buf = AES_BLOCK_SIZE; - - // Format the Associated Data, aka, assoc[]. - ccm_format_assoc_data(buf, &end_of_buf, assoc, assoc_len); - - // Format the Payload, aka payload[]. - ccm_format_payload_data(buf, &end_of_buf, payload, payload_len); - - // Create the first counter block. - ccm_prepare_first_ctr_blk(counter, nonce, nonce_len, payload_len_store_size); - - // Perform the CBC operation with an IV of zeros on the formatted buffer to calculate the MAC. - memset(temp_iv, 0, AES_BLOCK_SIZE); - aes_encrypt_cbc_mac(buf, end_of_buf, mac, key, keysize, temp_iv); - - // Copy the Payload and MAC to the output buffer. - memcpy(out, payload, payload_len); - memcpy(&out[payload_len], mac, mac_len); - - // Encrypt the Payload with CTR mode with a counter starting at 1. - memcpy(temp_iv, counter, AES_BLOCK_SIZE); - increment_iv(temp_iv, AES_BLOCK_SIZE - 1 - mac_len); // Last argument is the byte size of the counting portion of the counter block. /*BUG?*/ - aes_encrypt_ctr(out, payload_len, out, key, keysize, temp_iv); - - // Encrypt the MAC with CTR mode with a counter starting at 0. - aes_encrypt_ctr(&out[payload_len], mac_len, &out[payload_len], key, keysize, counter); - - free(buf); - *out_len = payload_len + mac_len; - - return(TRUE); -} - -// plaintext_len = ciphertext_len - mac_len -// Needs a flag for whether the MAC matches. -int aes_decrypt_ccm(const BYTE ciphertext[], WORD ciphertext_len, const BYTE assoc[], unsigned short assoc_len, - const BYTE nonce[], unsigned short nonce_len, BYTE plaintext[], WORD *plaintext_len, - WORD mac_len, int *mac_auth, const BYTE key_str[], int keysize) -{ - BYTE temp_iv[AES_BLOCK_SIZE], counter[AES_BLOCK_SIZE], mac[16], mac_buf[16], *buf; - int end_of_buf, plaintext_len_store_size; - WORD key[60]; - - if (ciphertext_len <= mac_len) - return(FALSE); - - buf = (BYTE*)malloc(assoc_len + ciphertext_len /*ciphertext_len = plaintext_len + mac_len*/ + 48); - if (! buf) - return(FALSE); - - // Prepare the key for usage. - aes_key_setup(key_str, key, keysize); - - // Copy the plaintext and MAC to the output buffers. - *plaintext_len = ciphertext_len - mac_len; - plaintext_len_store_size = AES_BLOCK_SIZE - 1 - nonce_len; - memcpy(plaintext, ciphertext, *plaintext_len); - memcpy(mac, &ciphertext[*plaintext_len], mac_len); - - // Prepare the first counter block for use in decryption. - ccm_prepare_first_ctr_blk(counter, nonce, nonce_len, plaintext_len_store_size); - - // Decrypt the Payload with CTR mode with a counter starting at 1. - memcpy(temp_iv, counter, AES_BLOCK_SIZE); - increment_iv(temp_iv, AES_BLOCK_SIZE - 1 - mac_len); // (AES_BLOCK_SIZE - 1 - mac_len) is the byte size of the counting portion of the counter block. - aes_decrypt_ctr(plaintext, *plaintext_len, plaintext, key, keysize, temp_iv); - - // Setting mac_auth to NULL disables the authentication check. - if (mac_auth != NULL) { - // Decrypt the MAC with CTR mode with a counter starting at 0. - aes_decrypt_ctr(mac, mac_len, mac, key, keysize, counter); - +int +aes_encrypt_ccm(const BYTE payload[], WORD payload_len, const BYTE assoc[], unsigned short assoc_len, + const BYTE nonce[], unsigned short nonce_len, BYTE out[], WORD *out_len, WORD mac_len, + const BYTE key_str[], int keysize) { + BYTE temp_iv[AES_BLOCK_SIZE], counter[AES_BLOCK_SIZE], mac[16], *buf; + int end_of_buf, payload_len_store_size; + WORD key[60]; + + if (mac_len != 4 && mac_len != 6 && mac_len != 8 && mac_len != 10 && mac_len != 12 && mac_len != 14 && + mac_len != 16) + return (FALSE); + + if (nonce_len < 7 || nonce_len > 13) + return (FALSE); + + if (assoc_len > 32768 /* = 2^15 */) + return (FALSE); + + buf = (BYTE *)malloc(payload_len + assoc_len + + 48 /*Round both payload and associated data up a block size and add an extra block.*/); + if (!buf) + return (FALSE); + + // Prepare the key for usage. + aes_key_setup(key_str, key, keysize); + // Format the first block of the formatted data. - plaintext_len_store_size = AES_BLOCK_SIZE - 1 - nonce_len; - ccm_prepare_first_format_blk(buf, assoc_len, *plaintext_len, plaintext_len_store_size, mac_len, nonce, nonce_len); + payload_len_store_size = AES_BLOCK_SIZE - 1 - nonce_len; + ccm_prepare_first_format_blk(buf, assoc_len, payload_len, payload_len_store_size, mac_len, nonce, nonce_len); end_of_buf = AES_BLOCK_SIZE; - - // Format the Associated Data into the authentication buffer. + + // Format the Associated Data, aka, assoc[]. ccm_format_assoc_data(buf, &end_of_buf, assoc, assoc_len); - - // Format the Payload into the authentication buffer. - ccm_format_payload_data(buf, &end_of_buf, plaintext, *plaintext_len); - + + // Format the Payload, aka payload[]. + ccm_format_payload_data(buf, &end_of_buf, payload, payload_len); + + // Create the first counter block. + ccm_prepare_first_ctr_blk(counter, nonce, nonce_len, payload_len_store_size); + // Perform the CBC operation with an IV of zeros on the formatted buffer to calculate the MAC. memset(temp_iv, 0, AES_BLOCK_SIZE); - aes_encrypt_cbc_mac(buf, end_of_buf, mac_buf, key, keysize, temp_iv); - - // Compare the calculated MAC against the MAC embedded in the ciphertext to see if they are the same. - if (! memcmp(mac, mac_buf, mac_len)) { - *mac_auth = TRUE; - } - else { - *mac_auth = FALSE; - memset(plaintext, 0, *plaintext_len); + aes_encrypt_cbc_mac(buf, end_of_buf, mac, key, keysize, temp_iv); + + // Copy the Payload and MAC to the output buffer. + memcpy(out, payload, payload_len); + memcpy(&out[payload_len], mac, mac_len); + + // Encrypt the Payload with CTR mode with a counter starting at 1. + memcpy(temp_iv, counter, AES_BLOCK_SIZE); + increment_iv( + temp_iv, + AES_BLOCK_SIZE - 1 - + mac_len); // Last argument is the byte size of the counting portion of the counter block. /*BUG?*/ + aes_encrypt_ctr(out, payload_len, out, key, keysize, temp_iv); + + // Encrypt the MAC with CTR mode with a counter starting at 0. + aes_encrypt_ctr(&out[payload_len], mac_len, &out[payload_len], key, keysize, counter); + + free(buf); + *out_len = payload_len + mac_len; + + return (TRUE); +} + +// plaintext_len = ciphertext_len - mac_len +// Needs a flag for whether the MAC matches. +int +aes_decrypt_ccm(const BYTE ciphertext[], WORD ciphertext_len, const BYTE assoc[], unsigned short assoc_len, + const BYTE nonce[], unsigned short nonce_len, BYTE plaintext[], WORD *plaintext_len, WORD mac_len, + int *mac_auth, const BYTE key_str[], int keysize) { + BYTE temp_iv[AES_BLOCK_SIZE], counter[AES_BLOCK_SIZE], mac[16], mac_buf[16], *buf; + int end_of_buf, plaintext_len_store_size; + WORD key[60]; + + if (ciphertext_len <= mac_len) + return (FALSE); + + buf = (BYTE *)malloc(assoc_len + ciphertext_len /*ciphertext_len = plaintext_len + mac_len*/ + 48); + if (!buf) + return (FALSE); + + // Prepare the key for usage. + aes_key_setup(key_str, key, keysize); + + // Copy the plaintext and MAC to the output buffers. + *plaintext_len = ciphertext_len - mac_len; + plaintext_len_store_size = AES_BLOCK_SIZE - 1 - nonce_len; + memcpy(plaintext, ciphertext, *plaintext_len); + memcpy(mac, &ciphertext[*plaintext_len], mac_len); + + // Prepare the first counter block for use in decryption. + ccm_prepare_first_ctr_blk(counter, nonce, nonce_len, plaintext_len_store_size); + + // Decrypt the Payload with CTR mode with a counter starting at 1. + memcpy(temp_iv, counter, AES_BLOCK_SIZE); + increment_iv(temp_iv, AES_BLOCK_SIZE - 1 - mac_len); // (AES_BLOCK_SIZE - 1 - mac_len) is the byte size of the + // counting portion of the counter block. + aes_decrypt_ctr(plaintext, *plaintext_len, plaintext, key, keysize, temp_iv); + + // Setting mac_auth to NULL disables the authentication check. + if (mac_auth != NULL) { + // Decrypt the MAC with CTR mode with a counter starting at 0. + aes_decrypt_ctr(mac, mac_len, mac, key, keysize, counter); + + // Format the first block of the formatted data. + plaintext_len_store_size = AES_BLOCK_SIZE - 1 - nonce_len; + ccm_prepare_first_format_blk(buf, assoc_len, *plaintext_len, plaintext_len_store_size, mac_len, nonce, + nonce_len); + end_of_buf = AES_BLOCK_SIZE; + + // Format the Associated Data into the authentication buffer. + ccm_format_assoc_data(buf, &end_of_buf, assoc, assoc_len); + + // Format the Payload into the authentication buffer. + ccm_format_payload_data(buf, &end_of_buf, plaintext, *plaintext_len); + + // Perform the CBC operation with an IV of zeros on the formatted buffer to calculate the MAC. + memset(temp_iv, 0, AES_BLOCK_SIZE); + aes_encrypt_cbc_mac(buf, end_of_buf, mac_buf, key, keysize, temp_iv); + + // Compare the calculated MAC against the MAC embedded in the ciphertext to see if they are the same. + if (!memcmp(mac, mac_buf, mac_len)) { + *mac_auth = TRUE; + } else { + *mac_auth = FALSE; + memset(plaintext, 0, *plaintext_len); + } } - } - - free(buf); - - return(TRUE); + + free(buf); + + return (TRUE); } - + // Creates the first counter block. First byte is flags, then the nonce, then the incremented part. -void ccm_prepare_first_ctr_blk(BYTE counter[], const BYTE nonce[], int nonce_len, int payload_len_store_size) -{ - memset(counter, 0, AES_BLOCK_SIZE); - counter[0] = (payload_len_store_size - 1) & 0x07; - memcpy(&counter[1], nonce, nonce_len); +void +ccm_prepare_first_ctr_blk(BYTE counter[], const BYTE nonce[], int nonce_len, int payload_len_store_size) { + memset(counter, 0, AES_BLOCK_SIZE); + counter[0] = (payload_len_store_size - 1) & 0x07; + memcpy(&counter[1], nonce, nonce_len); } - -void ccm_prepare_first_format_blk(BYTE buf[], int assoc_len, int payload_len, int payload_len_store_size, int mac_len, const BYTE nonce[], int nonce_len) -{ - // Set the flags for the first byte of the first block. - buf[0] = ((((mac_len - 2) / 2) & 0x07) << 3) | ((payload_len_store_size - 1) & 0x07); - if (assoc_len > 0) - buf[0] += 0x40; - // Format the rest of the first block, storing the nonce and the size of the payload. - memcpy(&buf[1], nonce, nonce_len); - memset(&buf[1 + nonce_len], 0, AES_BLOCK_SIZE - 1 - nonce_len); - buf[15] = payload_len & 0x000000FF; - buf[14] = (payload_len >> 8) & 0x000000FF; + +void +ccm_prepare_first_format_blk(BYTE buf[], int assoc_len, int payload_len, int payload_len_store_size, int mac_len, + const BYTE nonce[], int nonce_len) { + // Set the flags for the first byte of the first block. + buf[0] = ((((mac_len - 2) / 2) & 0x07) << 3) | ((payload_len_store_size - 1) & 0x07); + if (assoc_len > 0) + buf[0] += 0x40; + // Format the rest of the first block, storing the nonce and the size of the payload. + memcpy(&buf[1], nonce, nonce_len); + memset(&buf[1 + nonce_len], 0, AES_BLOCK_SIZE - 1 - nonce_len); + buf[15] = payload_len & 0x000000FF; + buf[14] = (payload_len >> 8) & 0x000000FF; } - -void ccm_format_assoc_data(BYTE buf[], int *end_of_buf, const BYTE assoc[], int assoc_len) -{ - int pad; - - buf[*end_of_buf + 1] = assoc_len & 0x00FF; - buf[*end_of_buf] = (assoc_len >> 8) & 0x00FF; - *end_of_buf += 2; - memcpy(&buf[*end_of_buf], assoc, assoc_len); - *end_of_buf += assoc_len; - pad = AES_BLOCK_SIZE - (*end_of_buf % AES_BLOCK_SIZE); /*BUG?*/ - memset(&buf[*end_of_buf], 0, pad); - *end_of_buf += pad; + +void +ccm_format_assoc_data(BYTE buf[], int *end_of_buf, const BYTE assoc[], int assoc_len) { + int pad; + + buf[*end_of_buf + 1] = assoc_len & 0x00FF; + buf[*end_of_buf] = (assoc_len >> 8) & 0x00FF; + *end_of_buf += 2; + memcpy(&buf[*end_of_buf], assoc, assoc_len); + *end_of_buf += assoc_len; + pad = AES_BLOCK_SIZE - (*end_of_buf % AES_BLOCK_SIZE); /*BUG?*/ + memset(&buf[*end_of_buf], 0, pad); + *end_of_buf += pad; } - -void ccm_format_payload_data(BYTE buf[], int *end_of_buf, const BYTE payload[], int payload_len) -{ - int pad; - - memcpy(&buf[*end_of_buf], payload, payload_len); - *end_of_buf += payload_len; - pad = *end_of_buf % AES_BLOCK_SIZE; - if (pad != 0) - pad = AES_BLOCK_SIZE - pad; - memset(&buf[*end_of_buf], 0, pad); - *end_of_buf += pad; + +void +ccm_format_payload_data(BYTE buf[], int *end_of_buf, const BYTE payload[], int payload_len) { + int pad; + + memcpy(&buf[*end_of_buf], payload, payload_len); + *end_of_buf += payload_len; + pad = *end_of_buf % AES_BLOCK_SIZE; + if (pad != 0) + pad = AES_BLOCK_SIZE - pad; + memset(&buf[*end_of_buf], 0, pad); + *end_of_buf += pad; } - + /******************* * AES *******************/ ///////////////// // KEY EXPANSION ///////////////// - + // Substitutes a word using the AES S-Box. -WORD SubWord(WORD word) -{ - unsigned int result; - - result = (int)aes_sbox[(word >> 4) & 0x0000000F][word & 0x0000000F]; - result += (int)aes_sbox[(word >> 12) & 0x0000000F][(word >> 8) & 0x0000000F] << 8; - result += (int)aes_sbox[(word >> 20) & 0x0000000F][(word >> 16) & 0x0000000F] << 16; - result += (int)aes_sbox[(word >> 28) & 0x0000000F][(word >> 24) & 0x0000000F] << 24; - return(result); +WORD +SubWord(WORD word) { + unsigned int result; + + result = (int)aes_sbox[(word >> 4) & 0x0000000F][word & 0x0000000F]; + result += (int)aes_sbox[(word >> 12) & 0x0000000F][(word >> 8) & 0x0000000F] << 8; + result += (int)aes_sbox[(word >> 20) & 0x0000000F][(word >> 16) & 0x0000000F] << 16; + result += (int)aes_sbox[(word >> 28) & 0x0000000F][(word >> 24) & 0x0000000F] << 24; + return (result); } - + // Performs the action of generating the keys that will be used in every round of // encryption. "key" is the user-supplied input key, "w" is the output key schedule, // "keysize" is the length in bits of "key", must be 128, 192, or 256. -void aes_key_setup(const BYTE key[], WORD w[], int keysize) -{ - int Nb=4,Nr,Nk,idx; - WORD temp,Rcon[]={0x01000000,0x02000000,0x04000000,0x08000000,0x10000000,0x20000000, - 0x40000000,0x80000000,0x1b000000,0x36000000,0x6c000000,0xd8000000, - 0xab000000,0x4d000000,0x9a000000}; - - switch (keysize) { - case 128: Nr = 10; Nk = 4; break; - case 192: Nr = 12; Nk = 6; break; - case 256: Nr = 14; Nk = 8; break; - default: return; - } - - for (idx=0; idx < Nk; ++idx) { - w[idx] = ((key[4 * idx]) << 24) | ((key[4 * idx + 1]) << 16) | - ((key[4 * idx + 2]) << 8) | ((key[4 * idx + 3])); - } - - for (idx = Nk; idx < Nb * (Nr+1); ++idx) { - temp = w[idx - 1]; - if ((idx % Nk) == 0) - temp = SubWord(KE_ROTWORD(temp)) ^ Rcon[(idx-1)/Nk]; - else if (Nk > 6 && (idx % Nk) == 4) - temp = SubWord(temp); - w[idx] = w[idx-Nk] ^ temp; - } +void +aes_key_setup(const BYTE key[], WORD w[], int keysize) { + int Nb = 4, Nr, Nk, idx; + WORD temp, + Rcon[] = {0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000, + 0x1b000000, 0x36000000, 0x6c000000, 0xd8000000, 0xab000000, 0x4d000000, 0x9a000000}; + + switch (keysize) { + case 128: + Nr = 10; + Nk = 4; + break; + case 192: + Nr = 12; + Nk = 6; + break; + case 256: + Nr = 14; + Nk = 8; + break; + default: + return; + } + + for (idx = 0; idx < Nk; ++idx) { + w[idx] = ((key[4 * idx]) << 24) | ((key[4 * idx + 1]) << 16) | ((key[4 * idx + 2]) << 8) | + ((key[4 * idx + 3])); + } + + for (idx = Nk; idx < Nb * (Nr + 1); ++idx) { + temp = w[idx - 1]; + if ((idx % Nk) == 0) + temp = SubWord(KE_ROTWORD(temp)) ^ Rcon[(idx - 1) / Nk]; + else if (Nk > 6 && (idx % Nk) == 4) + temp = SubWord(temp); + w[idx] = w[idx - Nk] ^ temp; + } } - + ///////////////// // ADD ROUND KEY ///////////////// - + // Performs the AddRoundKey step. Each round has its own pre-generated 16-byte key in the // form of 4 integers (the "w" array). Each integer is XOR'd by one column of the state. // Also performs the job of InvAddRoundKey(); since the function is a simple XOR process, // it is its own inverse. -void AddRoundKey(BYTE state[][4], const WORD w[]) -{ - BYTE subkey[4]; - - // memcpy(subkey,&w[idx],4); // Not accurate for big endian machines - // Subkey 1 - subkey[0] = w[0] >> 24; - subkey[1] = w[0] >> 16; - subkey[2] = w[0] >> 8; - subkey[3] = w[0]; - state[0][0] ^= subkey[0]; - state[1][0] ^= subkey[1]; - state[2][0] ^= subkey[2]; - state[3][0] ^= subkey[3]; - // Subkey 2 - subkey[0] = w[1] >> 24; - subkey[1] = w[1] >> 16; - subkey[2] = w[1] >> 8; - subkey[3] = w[1]; - state[0][1] ^= subkey[0]; - state[1][1] ^= subkey[1]; - state[2][1] ^= subkey[2]; - state[3][1] ^= subkey[3]; - // Subkey 3 - subkey[0] = w[2] >> 24; - subkey[1] = w[2] >> 16; - subkey[2] = w[2] >> 8; - subkey[3] = w[2]; - state[0][2] ^= subkey[0]; - state[1][2] ^= subkey[1]; - state[2][2] ^= subkey[2]; - state[3][2] ^= subkey[3]; - // Subkey 4 - subkey[0] = w[3] >> 24; - subkey[1] = w[3] >> 16; - subkey[2] = w[3] >> 8; - subkey[3] = w[3]; - state[0][3] ^= subkey[0]; - state[1][3] ^= subkey[1]; - state[2][3] ^= subkey[2]; - state[3][3] ^= subkey[3]; +void +AddRoundKey(BYTE state[][4], const WORD w[]) { + BYTE subkey[4]; + + // memcpy(subkey,&w[idx],4); // Not accurate for big endian machines + // Subkey 1 + subkey[0] = w[0] >> 24; + subkey[1] = w[0] >> 16; + subkey[2] = w[0] >> 8; + subkey[3] = w[0]; + state[0][0] ^= subkey[0]; + state[1][0] ^= subkey[1]; + state[2][0] ^= subkey[2]; + state[3][0] ^= subkey[3]; + // Subkey 2 + subkey[0] = w[1] >> 24; + subkey[1] = w[1] >> 16; + subkey[2] = w[1] >> 8; + subkey[3] = w[1]; + state[0][1] ^= subkey[0]; + state[1][1] ^= subkey[1]; + state[2][1] ^= subkey[2]; + state[3][1] ^= subkey[3]; + // Subkey 3 + subkey[0] = w[2] >> 24; + subkey[1] = w[2] >> 16; + subkey[2] = w[2] >> 8; + subkey[3] = w[2]; + state[0][2] ^= subkey[0]; + state[1][2] ^= subkey[1]; + state[2][2] ^= subkey[2]; + state[3][2] ^= subkey[3]; + // Subkey 4 + subkey[0] = w[3] >> 24; + subkey[1] = w[3] >> 16; + subkey[2] = w[3] >> 8; + subkey[3] = w[3]; + state[0][3] ^= subkey[0]; + state[1][3] ^= subkey[1]; + state[2][3] ^= subkey[2]; + state[3][3] ^= subkey[3]; } - + ///////////////// // (Inv)SubBytes ///////////////// - + // Performs the SubBytes step. All bytes in the state are substituted with a // pre-calculated value from a lookup table. -void SubBytes(BYTE state[][4]) -{ - state[0][0] = aes_sbox[state[0][0] >> 4][state[0][0] & 0x0F]; - state[0][1] = aes_sbox[state[0][1] >> 4][state[0][1] & 0x0F]; - state[0][2] = aes_sbox[state[0][2] >> 4][state[0][2] & 0x0F]; - state[0][3] = aes_sbox[state[0][3] >> 4][state[0][3] & 0x0F]; - state[1][0] = aes_sbox[state[1][0] >> 4][state[1][0] & 0x0F]; - state[1][1] = aes_sbox[state[1][1] >> 4][state[1][1] & 0x0F]; - state[1][2] = aes_sbox[state[1][2] >> 4][state[1][2] & 0x0F]; - state[1][3] = aes_sbox[state[1][3] >> 4][state[1][3] & 0x0F]; - state[2][0] = aes_sbox[state[2][0] >> 4][state[2][0] & 0x0F]; - state[2][1] = aes_sbox[state[2][1] >> 4][state[2][1] & 0x0F]; - state[2][2] = aes_sbox[state[2][2] >> 4][state[2][2] & 0x0F]; - state[2][3] = aes_sbox[state[2][3] >> 4][state[2][3] & 0x0F]; - state[3][0] = aes_sbox[state[3][0] >> 4][state[3][0] & 0x0F]; - state[3][1] = aes_sbox[state[3][1] >> 4][state[3][1] & 0x0F]; - state[3][2] = aes_sbox[state[3][2] >> 4][state[3][2] & 0x0F]; - state[3][3] = aes_sbox[state[3][3] >> 4][state[3][3] & 0x0F]; +void +SubBytes(BYTE state[][4]) { + state[0][0] = aes_sbox[state[0][0] >> 4][state[0][0] & 0x0F]; + state[0][1] = aes_sbox[state[0][1] >> 4][state[0][1] & 0x0F]; + state[0][2] = aes_sbox[state[0][2] >> 4][state[0][2] & 0x0F]; + state[0][3] = aes_sbox[state[0][3] >> 4][state[0][3] & 0x0F]; + state[1][0] = aes_sbox[state[1][0] >> 4][state[1][0] & 0x0F]; + state[1][1] = aes_sbox[state[1][1] >> 4][state[1][1] & 0x0F]; + state[1][2] = aes_sbox[state[1][2] >> 4][state[1][2] & 0x0F]; + state[1][3] = aes_sbox[state[1][3] >> 4][state[1][3] & 0x0F]; + state[2][0] = aes_sbox[state[2][0] >> 4][state[2][0] & 0x0F]; + state[2][1] = aes_sbox[state[2][1] >> 4][state[2][1] & 0x0F]; + state[2][2] = aes_sbox[state[2][2] >> 4][state[2][2] & 0x0F]; + state[2][3] = aes_sbox[state[2][3] >> 4][state[2][3] & 0x0F]; + state[3][0] = aes_sbox[state[3][0] >> 4][state[3][0] & 0x0F]; + state[3][1] = aes_sbox[state[3][1] >> 4][state[3][1] & 0x0F]; + state[3][2] = aes_sbox[state[3][2] >> 4][state[3][2] & 0x0F]; + state[3][3] = aes_sbox[state[3][3] >> 4][state[3][3] & 0x0F]; } - -void InvSubBytes(BYTE state[][4]) -{ - state[0][0] = aes_invsbox[state[0][0] >> 4][state[0][0] & 0x0F]; - state[0][1] = aes_invsbox[state[0][1] >> 4][state[0][1] & 0x0F]; - state[0][2] = aes_invsbox[state[0][2] >> 4][state[0][2] & 0x0F]; - state[0][3] = aes_invsbox[state[0][3] >> 4][state[0][3] & 0x0F]; - state[1][0] = aes_invsbox[state[1][0] >> 4][state[1][0] & 0x0F]; - state[1][1] = aes_invsbox[state[1][1] >> 4][state[1][1] & 0x0F]; - state[1][2] = aes_invsbox[state[1][2] >> 4][state[1][2] & 0x0F]; - state[1][3] = aes_invsbox[state[1][3] >> 4][state[1][3] & 0x0F]; - state[2][0] = aes_invsbox[state[2][0] >> 4][state[2][0] & 0x0F]; - state[2][1] = aes_invsbox[state[2][1] >> 4][state[2][1] & 0x0F]; - state[2][2] = aes_invsbox[state[2][2] >> 4][state[2][2] & 0x0F]; - state[2][3] = aes_invsbox[state[2][3] >> 4][state[2][3] & 0x0F]; - state[3][0] = aes_invsbox[state[3][0] >> 4][state[3][0] & 0x0F]; - state[3][1] = aes_invsbox[state[3][1] >> 4][state[3][1] & 0x0F]; - state[3][2] = aes_invsbox[state[3][2] >> 4][state[3][2] & 0x0F]; - state[3][3] = aes_invsbox[state[3][3] >> 4][state[3][3] & 0x0F]; + +void +InvSubBytes(BYTE state[][4]) { + state[0][0] = aes_invsbox[state[0][0] >> 4][state[0][0] & 0x0F]; + state[0][1] = aes_invsbox[state[0][1] >> 4][state[0][1] & 0x0F]; + state[0][2] = aes_invsbox[state[0][2] >> 4][state[0][2] & 0x0F]; + state[0][3] = aes_invsbox[state[0][3] >> 4][state[0][3] & 0x0F]; + state[1][0] = aes_invsbox[state[1][0] >> 4][state[1][0] & 0x0F]; + state[1][1] = aes_invsbox[state[1][1] >> 4][state[1][1] & 0x0F]; + state[1][2] = aes_invsbox[state[1][2] >> 4][state[1][2] & 0x0F]; + state[1][3] = aes_invsbox[state[1][3] >> 4][state[1][3] & 0x0F]; + state[2][0] = aes_invsbox[state[2][0] >> 4][state[2][0] & 0x0F]; + state[2][1] = aes_invsbox[state[2][1] >> 4][state[2][1] & 0x0F]; + state[2][2] = aes_invsbox[state[2][2] >> 4][state[2][2] & 0x0F]; + state[2][3] = aes_invsbox[state[2][3] >> 4][state[2][3] & 0x0F]; + state[3][0] = aes_invsbox[state[3][0] >> 4][state[3][0] & 0x0F]; + state[3][1] = aes_invsbox[state[3][1] >> 4][state[3][1] & 0x0F]; + state[3][2] = aes_invsbox[state[3][2] >> 4][state[3][2] & 0x0F]; + state[3][3] = aes_invsbox[state[3][3] >> 4][state[3][3] & 0x0F]; } - + ///////////////// // (Inv)ShiftRows ///////////////// - + // Performs the ShiftRows step. All rows are shifted cylindrically to the left. -void ShiftRows(BYTE state[][4]) -{ - int t; - - // Shift left by 1 - t = state[1][0]; - state[1][0] = state[1][1]; - state[1][1] = state[1][2]; - state[1][2] = state[1][3]; - state[1][3] = t; - // Shift left by 2 - t = state[2][0]; - state[2][0] = state[2][2]; - state[2][2] = t; - t = state[2][1]; - state[2][1] = state[2][3]; - state[2][3] = t; - // Shift left by 3 - t = state[3][0]; - state[3][0] = state[3][3]; - state[3][3] = state[3][2]; - state[3][2] = state[3][1]; - state[3][1] = t; +void +ShiftRows(BYTE state[][4]) { + int t; + + // Shift left by 1 + t = state[1][0]; + state[1][0] = state[1][1]; + state[1][1] = state[1][2]; + state[1][2] = state[1][3]; + state[1][3] = t; + // Shift left by 2 + t = state[2][0]; + state[2][0] = state[2][2]; + state[2][2] = t; + t = state[2][1]; + state[2][1] = state[2][3]; + state[2][3] = t; + // Shift left by 3 + t = state[3][0]; + state[3][0] = state[3][3]; + state[3][3] = state[3][2]; + state[3][2] = state[3][1]; + state[3][1] = t; } - + // All rows are shifted cylindrically to the right. -void InvShiftRows(BYTE state[][4]) -{ - int t; - - // Shift right by 1 - t = state[1][3]; - state[1][3] = state[1][2]; - state[1][2] = state[1][1]; - state[1][1] = state[1][0]; - state[1][0] = t; - // Shift right by 2 - t = state[2][3]; - state[2][3] = state[2][1]; - state[2][1] = t; - t = state[2][2]; - state[2][2] = state[2][0]; - state[2][0] = t; - // Shift right by 3 - t = state[3][3]; - state[3][3] = state[3][0]; - state[3][0] = state[3][1]; - state[3][1] = state[3][2]; - state[3][2] = t; +void +InvShiftRows(BYTE state[][4]) { + int t; + + // Shift right by 1 + t = state[1][3]; + state[1][3] = state[1][2]; + state[1][2] = state[1][1]; + state[1][1] = state[1][0]; + state[1][0] = t; + // Shift right by 2 + t = state[2][3]; + state[2][3] = state[2][1]; + state[2][1] = t; + t = state[2][2]; + state[2][2] = state[2][0]; + state[2][0] = t; + // Shift right by 3 + t = state[3][3]; + state[3][3] = state[3][0]; + state[3][0] = state[3][1]; + state[3][1] = state[3][2]; + state[3][2] = t; } - + ///////////////// // (Inv)MixColumns ///////////////// - + // Performs the MixColums step. The state is multiplied by itself using matrix // multiplication in a Galios Field 2^8. All multiplication is pre-computed in a table. // Addition is equivilent to XOR. (Must always make a copy of the column as the original // values will be destoyed.) -void MixColumns(BYTE state[][4]) -{ - BYTE col[4]; - - // Column 1 - col[0] = state[0][0]; - col[1] = state[1][0]; - col[2] = state[2][0]; - col[3] = state[3][0]; - state[0][0] = gf_mul[col[0]][0]; - state[0][0] ^= gf_mul[col[1]][1]; - state[0][0] ^= col[2]; - state[0][0] ^= col[3]; - state[1][0] = col[0]; - state[1][0] ^= gf_mul[col[1]][0]; - state[1][0] ^= gf_mul[col[2]][1]; - state[1][0] ^= col[3]; - state[2][0] = col[0]; - state[2][0] ^= col[1]; - state[2][0] ^= gf_mul[col[2]][0]; - state[2][0] ^= gf_mul[col[3]][1]; - state[3][0] = gf_mul[col[0]][1]; - state[3][0] ^= col[1]; - state[3][0] ^= col[2]; - state[3][0] ^= gf_mul[col[3]][0]; - // Column 2 - col[0] = state[0][1]; - col[1] = state[1][1]; - col[2] = state[2][1]; - col[3] = state[3][1]; - state[0][1] = gf_mul[col[0]][0]; - state[0][1] ^= gf_mul[col[1]][1]; - state[0][1] ^= col[2]; - state[0][1] ^= col[3]; - state[1][1] = col[0]; - state[1][1] ^= gf_mul[col[1]][0]; - state[1][1] ^= gf_mul[col[2]][1]; - state[1][1] ^= col[3]; - state[2][1] = col[0]; - state[2][1] ^= col[1]; - state[2][1] ^= gf_mul[col[2]][0]; - state[2][1] ^= gf_mul[col[3]][1]; - state[3][1] = gf_mul[col[0]][1]; - state[3][1] ^= col[1]; - state[3][1] ^= col[2]; - state[3][1] ^= gf_mul[col[3]][0]; - // Column 3 - col[0] = state[0][2]; - col[1] = state[1][2]; - col[2] = state[2][2]; - col[3] = state[3][2]; - state[0][2] = gf_mul[col[0]][0]; - state[0][2] ^= gf_mul[col[1]][1]; - state[0][2] ^= col[2]; - state[0][2] ^= col[3]; - state[1][2] = col[0]; - state[1][2] ^= gf_mul[col[1]][0]; - state[1][2] ^= gf_mul[col[2]][1]; - state[1][2] ^= col[3]; - state[2][2] = col[0]; - state[2][2] ^= col[1]; - state[2][2] ^= gf_mul[col[2]][0]; - state[2][2] ^= gf_mul[col[3]][1]; - state[3][2] = gf_mul[col[0]][1]; - state[3][2] ^= col[1]; - state[3][2] ^= col[2]; - state[3][2] ^= gf_mul[col[3]][0]; - // Column 4 - col[0] = state[0][3]; - col[1] = state[1][3]; - col[2] = state[2][3]; - col[3] = state[3][3]; - state[0][3] = gf_mul[col[0]][0]; - state[0][3] ^= gf_mul[col[1]][1]; - state[0][3] ^= col[2]; - state[0][3] ^= col[3]; - state[1][3] = col[0]; - state[1][3] ^= gf_mul[col[1]][0]; - state[1][3] ^= gf_mul[col[2]][1]; - state[1][3] ^= col[3]; - state[2][3] = col[0]; - state[2][3] ^= col[1]; - state[2][3] ^= gf_mul[col[2]][0]; - state[2][3] ^= gf_mul[col[3]][1]; - state[3][3] = gf_mul[col[0]][1]; - state[3][3] ^= col[1]; - state[3][3] ^= col[2]; - state[3][3] ^= gf_mul[col[3]][0]; +void +MixColumns(BYTE state[][4]) { + BYTE col[4]; + + // Column 1 + col[0] = state[0][0]; + col[1] = state[1][0]; + col[2] = state[2][0]; + col[3] = state[3][0]; + state[0][0] = gf_mul[col[0]][0]; + state[0][0] ^= gf_mul[col[1]][1]; + state[0][0] ^= col[2]; + state[0][0] ^= col[3]; + state[1][0] = col[0]; + state[1][0] ^= gf_mul[col[1]][0]; + state[1][0] ^= gf_mul[col[2]][1]; + state[1][0] ^= col[3]; + state[2][0] = col[0]; + state[2][0] ^= col[1]; + state[2][0] ^= gf_mul[col[2]][0]; + state[2][0] ^= gf_mul[col[3]][1]; + state[3][0] = gf_mul[col[0]][1]; + state[3][0] ^= col[1]; + state[3][0] ^= col[2]; + state[3][0] ^= gf_mul[col[3]][0]; + // Column 2 + col[0] = state[0][1]; + col[1] = state[1][1]; + col[2] = state[2][1]; + col[3] = state[3][1]; + state[0][1] = gf_mul[col[0]][0]; + state[0][1] ^= gf_mul[col[1]][1]; + state[0][1] ^= col[2]; + state[0][1] ^= col[3]; + state[1][1] = col[0]; + state[1][1] ^= gf_mul[col[1]][0]; + state[1][1] ^= gf_mul[col[2]][1]; + state[1][1] ^= col[3]; + state[2][1] = col[0]; + state[2][1] ^= col[1]; + state[2][1] ^= gf_mul[col[2]][0]; + state[2][1] ^= gf_mul[col[3]][1]; + state[3][1] = gf_mul[col[0]][1]; + state[3][1] ^= col[1]; + state[3][1] ^= col[2]; + state[3][1] ^= gf_mul[col[3]][0]; + // Column 3 + col[0] = state[0][2]; + col[1] = state[1][2]; + col[2] = state[2][2]; + col[3] = state[3][2]; + state[0][2] = gf_mul[col[0]][0]; + state[0][2] ^= gf_mul[col[1]][1]; + state[0][2] ^= col[2]; + state[0][2] ^= col[3]; + state[1][2] = col[0]; + state[1][2] ^= gf_mul[col[1]][0]; + state[1][2] ^= gf_mul[col[2]][1]; + state[1][2] ^= col[3]; + state[2][2] = col[0]; + state[2][2] ^= col[1]; + state[2][2] ^= gf_mul[col[2]][0]; + state[2][2] ^= gf_mul[col[3]][1]; + state[3][2] = gf_mul[col[0]][1]; + state[3][2] ^= col[1]; + state[3][2] ^= col[2]; + state[3][2] ^= gf_mul[col[3]][0]; + // Column 4 + col[0] = state[0][3]; + col[1] = state[1][3]; + col[2] = state[2][3]; + col[3] = state[3][3]; + state[0][3] = gf_mul[col[0]][0]; + state[0][3] ^= gf_mul[col[1]][1]; + state[0][3] ^= col[2]; + state[0][3] ^= col[3]; + state[1][3] = col[0]; + state[1][3] ^= gf_mul[col[1]][0]; + state[1][3] ^= gf_mul[col[2]][1]; + state[1][3] ^= col[3]; + state[2][3] = col[0]; + state[2][3] ^= col[1]; + state[2][3] ^= gf_mul[col[2]][0]; + state[2][3] ^= gf_mul[col[3]][1]; + state[3][3] = gf_mul[col[0]][1]; + state[3][3] ^= col[1]; + state[3][3] ^= col[2]; + state[3][3] ^= gf_mul[col[3]][0]; } - -void InvMixColumns(BYTE state[][4]) -{ - BYTE col[4]; - - // Column 1 - col[0] = state[0][0]; - col[1] = state[1][0]; - col[2] = state[2][0]; - col[3] = state[3][0]; - state[0][0] = gf_mul[col[0]][5]; - state[0][0] ^= gf_mul[col[1]][3]; - state[0][0] ^= gf_mul[col[2]][4]; - state[0][0] ^= gf_mul[col[3]][2]; - state[1][0] = gf_mul[col[0]][2]; - state[1][0] ^= gf_mul[col[1]][5]; - state[1][0] ^= gf_mul[col[2]][3]; - state[1][0] ^= gf_mul[col[3]][4]; - state[2][0] = gf_mul[col[0]][4]; - state[2][0] ^= gf_mul[col[1]][2]; - state[2][0] ^= gf_mul[col[2]][5]; - state[2][0] ^= gf_mul[col[3]][3]; - state[3][0] = gf_mul[col[0]][3]; - state[3][0] ^= gf_mul[col[1]][4]; - state[3][0] ^= gf_mul[col[2]][2]; - state[3][0] ^= gf_mul[col[3]][5]; - // Column 2 - col[0] = state[0][1]; - col[1] = state[1][1]; - col[2] = state[2][1]; - col[3] = state[3][1]; - state[0][1] = gf_mul[col[0]][5]; - state[0][1] ^= gf_mul[col[1]][3]; - state[0][1] ^= gf_mul[col[2]][4]; - state[0][1] ^= gf_mul[col[3]][2]; - state[1][1] = gf_mul[col[0]][2]; - state[1][1] ^= gf_mul[col[1]][5]; - state[1][1] ^= gf_mul[col[2]][3]; - state[1][1] ^= gf_mul[col[3]][4]; - state[2][1] = gf_mul[col[0]][4]; - state[2][1] ^= gf_mul[col[1]][2]; - state[2][1] ^= gf_mul[col[2]][5]; - state[2][1] ^= gf_mul[col[3]][3]; - state[3][1] = gf_mul[col[0]][3]; - state[3][1] ^= gf_mul[col[1]][4]; - state[3][1] ^= gf_mul[col[2]][2]; - state[3][1] ^= gf_mul[col[3]][5]; - // Column 3 - col[0] = state[0][2]; - col[1] = state[1][2]; - col[2] = state[2][2]; - col[3] = state[3][2]; - state[0][2] = gf_mul[col[0]][5]; - state[0][2] ^= gf_mul[col[1]][3]; - state[0][2] ^= gf_mul[col[2]][4]; - state[0][2] ^= gf_mul[col[3]][2]; - state[1][2] = gf_mul[col[0]][2]; - state[1][2] ^= gf_mul[col[1]][5]; - state[1][2] ^= gf_mul[col[2]][3]; - state[1][2] ^= gf_mul[col[3]][4]; - state[2][2] = gf_mul[col[0]][4]; - state[2][2] ^= gf_mul[col[1]][2]; - state[2][2] ^= gf_mul[col[2]][5]; - state[2][2] ^= gf_mul[col[3]][3]; - state[3][2] = gf_mul[col[0]][3]; - state[3][2] ^= gf_mul[col[1]][4]; - state[3][2] ^= gf_mul[col[2]][2]; - state[3][2] ^= gf_mul[col[3]][5]; - // Column 4 - col[0] = state[0][3]; - col[1] = state[1][3]; - col[2] = state[2][3]; - col[3] = state[3][3]; - state[0][3] = gf_mul[col[0]][5]; - state[0][3] ^= gf_mul[col[1]][3]; - state[0][3] ^= gf_mul[col[2]][4]; - state[0][3] ^= gf_mul[col[3]][2]; - state[1][3] = gf_mul[col[0]][2]; - state[1][3] ^= gf_mul[col[1]][5]; - state[1][3] ^= gf_mul[col[2]][3]; - state[1][3] ^= gf_mul[col[3]][4]; - state[2][3] = gf_mul[col[0]][4]; - state[2][3] ^= gf_mul[col[1]][2]; - state[2][3] ^= gf_mul[col[2]][5]; - state[2][3] ^= gf_mul[col[3]][3]; - state[3][3] = gf_mul[col[0]][3]; - state[3][3] ^= gf_mul[col[1]][4]; - state[3][3] ^= gf_mul[col[2]][2]; - state[3][3] ^= gf_mul[col[3]][5]; + +void +InvMixColumns(BYTE state[][4]) { + BYTE col[4]; + + // Column 1 + col[0] = state[0][0]; + col[1] = state[1][0]; + col[2] = state[2][0]; + col[3] = state[3][0]; + state[0][0] = gf_mul[col[0]][5]; + state[0][0] ^= gf_mul[col[1]][3]; + state[0][0] ^= gf_mul[col[2]][4]; + state[0][0] ^= gf_mul[col[3]][2]; + state[1][0] = gf_mul[col[0]][2]; + state[1][0] ^= gf_mul[col[1]][5]; + state[1][0] ^= gf_mul[col[2]][3]; + state[1][0] ^= gf_mul[col[3]][4]; + state[2][0] = gf_mul[col[0]][4]; + state[2][0] ^= gf_mul[col[1]][2]; + state[2][0] ^= gf_mul[col[2]][5]; + state[2][0] ^= gf_mul[col[3]][3]; + state[3][0] = gf_mul[col[0]][3]; + state[3][0] ^= gf_mul[col[1]][4]; + state[3][0] ^= gf_mul[col[2]][2]; + state[3][0] ^= gf_mul[col[3]][5]; + // Column 2 + col[0] = state[0][1]; + col[1] = state[1][1]; + col[2] = state[2][1]; + col[3] = state[3][1]; + state[0][1] = gf_mul[col[0]][5]; + state[0][1] ^= gf_mul[col[1]][3]; + state[0][1] ^= gf_mul[col[2]][4]; + state[0][1] ^= gf_mul[col[3]][2]; + state[1][1] = gf_mul[col[0]][2]; + state[1][1] ^= gf_mul[col[1]][5]; + state[1][1] ^= gf_mul[col[2]][3]; + state[1][1] ^= gf_mul[col[3]][4]; + state[2][1] = gf_mul[col[0]][4]; + state[2][1] ^= gf_mul[col[1]][2]; + state[2][1] ^= gf_mul[col[2]][5]; + state[2][1] ^= gf_mul[col[3]][3]; + state[3][1] = gf_mul[col[0]][3]; + state[3][1] ^= gf_mul[col[1]][4]; + state[3][1] ^= gf_mul[col[2]][2]; + state[3][1] ^= gf_mul[col[3]][5]; + // Column 3 + col[0] = state[0][2]; + col[1] = state[1][2]; + col[2] = state[2][2]; + col[3] = state[3][2]; + state[0][2] = gf_mul[col[0]][5]; + state[0][2] ^= gf_mul[col[1]][3]; + state[0][2] ^= gf_mul[col[2]][4]; + state[0][2] ^= gf_mul[col[3]][2]; + state[1][2] = gf_mul[col[0]][2]; + state[1][2] ^= gf_mul[col[1]][5]; + state[1][2] ^= gf_mul[col[2]][3]; + state[1][2] ^= gf_mul[col[3]][4]; + state[2][2] = gf_mul[col[0]][4]; + state[2][2] ^= gf_mul[col[1]][2]; + state[2][2] ^= gf_mul[col[2]][5]; + state[2][2] ^= gf_mul[col[3]][3]; + state[3][2] = gf_mul[col[0]][3]; + state[3][2] ^= gf_mul[col[1]][4]; + state[3][2] ^= gf_mul[col[2]][2]; + state[3][2] ^= gf_mul[col[3]][5]; + // Column 4 + col[0] = state[0][3]; + col[1] = state[1][3]; + col[2] = state[2][3]; + col[3] = state[3][3]; + state[0][3] = gf_mul[col[0]][5]; + state[0][3] ^= gf_mul[col[1]][3]; + state[0][3] ^= gf_mul[col[2]][4]; + state[0][3] ^= gf_mul[col[3]][2]; + state[1][3] = gf_mul[col[0]][2]; + state[1][3] ^= gf_mul[col[1]][5]; + state[1][3] ^= gf_mul[col[2]][3]; + state[1][3] ^= gf_mul[col[3]][4]; + state[2][3] = gf_mul[col[0]][4]; + state[2][3] ^= gf_mul[col[1]][2]; + state[2][3] ^= gf_mul[col[2]][5]; + state[2][3] ^= gf_mul[col[3]][3]; + state[3][3] = gf_mul[col[0]][3]; + state[3][3] ^= gf_mul[col[1]][4]; + state[3][3] ^= gf_mul[col[2]][2]; + state[3][3] ^= gf_mul[col[3]][5]; } - + ///////////////// // (En/De)Crypt ///////////////// - -void aes_encrypt(const BYTE in[], BYTE out[], const WORD key[], int keysize) -{ - BYTE state[4][4]; - - // Copy input array (should be 16 bytes long) to a matrix (sequential bytes are ordered - // by row, not col) called "state" for processing. - // *** Implementation note: The official AES documentation references the state by - // column, then row. Accessing an element in C requires row then column. Thus, all state - // references in AES must have the column and row indexes reversed for C implementation. - state[0][0] = in[0]; - state[1][0] = in[1]; - state[2][0] = in[2]; - state[3][0] = in[3]; - state[0][1] = in[4]; - state[1][1] = in[5]; - state[2][1] = in[6]; - state[3][1] = in[7]; - state[0][2] = in[8]; - state[1][2] = in[9]; - state[2][2] = in[10]; - state[3][2] = in[11]; - state[0][3] = in[12]; - state[1][3] = in[13]; - state[2][3] = in[14]; - state[3][3] = in[15]; - - // Perform the necessary number of rounds. The round key is added first. - // The last round does not perform the MixColumns step. - AddRoundKey(state,&key[0]); - SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[4]); - SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[8]); - SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[12]); - SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[16]); - SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[20]); - SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[24]); - SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[28]); - SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[32]); - SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[36]); - if (keysize != 128) { - SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[40]); - SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[44]); - if (keysize != 192) { - SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[48]); - SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[52]); - SubBytes(state); ShiftRows(state); AddRoundKey(state,&key[56]); - } - else { - SubBytes(state); ShiftRows(state); AddRoundKey(state,&key[48]); + +void +aes_encrypt(const BYTE in[], BYTE out[], const WORD key[], int keysize) { + BYTE state[4][4]; + + // Copy input array (should be 16 bytes long) to a matrix (sequential bytes are ordered + // by row, not col) called "state" for processing. + // *** Implementation note: The official AES documentation references the state by + // column, then row. Accessing an element in C requires row then column. Thus, all state + // references in AES must have the column and row indexes reversed for C implementation. + state[0][0] = in[0]; + state[1][0] = in[1]; + state[2][0] = in[2]; + state[3][0] = in[3]; + state[0][1] = in[4]; + state[1][1] = in[5]; + state[2][1] = in[6]; + state[3][1] = in[7]; + state[0][2] = in[8]; + state[1][2] = in[9]; + state[2][2] = in[10]; + state[3][2] = in[11]; + state[0][3] = in[12]; + state[1][3] = in[13]; + state[2][3] = in[14]; + state[3][3] = in[15]; + + // Perform the necessary number of rounds. The round key is added first. + // The last round does not perform the MixColumns step. + AddRoundKey(state, &key[0]); + SubBytes(state); + ShiftRows(state); + MixColumns(state); + AddRoundKey(state, &key[4]); + SubBytes(state); + ShiftRows(state); + MixColumns(state); + AddRoundKey(state, &key[8]); + SubBytes(state); + ShiftRows(state); + MixColumns(state); + AddRoundKey(state, &key[12]); + SubBytes(state); + ShiftRows(state); + MixColumns(state); + AddRoundKey(state, &key[16]); + SubBytes(state); + ShiftRows(state); + MixColumns(state); + AddRoundKey(state, &key[20]); + SubBytes(state); + ShiftRows(state); + MixColumns(state); + AddRoundKey(state, &key[24]); + SubBytes(state); + ShiftRows(state); + MixColumns(state); + AddRoundKey(state, &key[28]); + SubBytes(state); + ShiftRows(state); + MixColumns(state); + AddRoundKey(state, &key[32]); + SubBytes(state); + ShiftRows(state); + MixColumns(state); + AddRoundKey(state, &key[36]); + if (keysize != 128) { + SubBytes(state); + ShiftRows(state); + MixColumns(state); + AddRoundKey(state, &key[40]); + SubBytes(state); + ShiftRows(state); + MixColumns(state); + AddRoundKey(state, &key[44]); + if (keysize != 192) { + SubBytes(state); + ShiftRows(state); + MixColumns(state); + AddRoundKey(state, &key[48]); + SubBytes(state); + ShiftRows(state); + MixColumns(state); + AddRoundKey(state, &key[52]); + SubBytes(state); + ShiftRows(state); + AddRoundKey(state, &key[56]); + } else { + SubBytes(state); + ShiftRows(state); + AddRoundKey(state, &key[48]); + } + } else { + SubBytes(state); + ShiftRows(state); + AddRoundKey(state, &key[40]); } - } - else { - SubBytes(state); ShiftRows(state); AddRoundKey(state,&key[40]); - } - - // Copy the state to the output array. - out[0] = state[0][0]; - out[1] = state[1][0]; - out[2] = state[2][0]; - out[3] = state[3][0]; - out[4] = state[0][1]; - out[5] = state[1][1]; - out[6] = state[2][1]; - out[7] = state[3][1]; - out[8] = state[0][2]; - out[9] = state[1][2]; - out[10] = state[2][2]; - out[11] = state[3][2]; - out[12] = state[0][3]; - out[13] = state[1][3]; - out[14] = state[2][3]; - out[15] = state[3][3]; + + // Copy the state to the output array. + out[0] = state[0][0]; + out[1] = state[1][0]; + out[2] = state[2][0]; + out[3] = state[3][0]; + out[4] = state[0][1]; + out[5] = state[1][1]; + out[6] = state[2][1]; + out[7] = state[3][1]; + out[8] = state[0][2]; + out[9] = state[1][2]; + out[10] = state[2][2]; + out[11] = state[3][2]; + out[12] = state[0][3]; + out[13] = state[1][3]; + out[14] = state[2][3]; + out[15] = state[3][3]; } - -void aes_decrypt(const BYTE in[], BYTE out[], const WORD key[], int keysize) -{ - BYTE state[4][4]; - - // Copy the input to the state. - state[0][0] = in[0]; - state[1][0] = in[1]; - state[2][0] = in[2]; - state[3][0] = in[3]; - state[0][1] = in[4]; - state[1][1] = in[5]; - state[2][1] = in[6]; - state[3][1] = in[7]; - state[0][2] = in[8]; - state[1][2] = in[9]; - state[2][2] = in[10]; - state[3][2] = in[11]; - state[0][3] = in[12]; - state[1][3] = in[13]; - state[2][3] = in[14]; - state[3][3] = in[15]; - - // Perform the necessary number of rounds. The round key is added first. - // The last round does not perform the MixColumns step. - if (keysize > 128) { - if (keysize > 192) { - AddRoundKey(state,&key[56]); - InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[52]);InvMixColumns(state); - InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[48]);InvMixColumns(state); - } - else { - AddRoundKey(state,&key[48]); + +void +aes_decrypt(const BYTE in[], BYTE out[], const WORD key[], int keysize) { + BYTE state[4][4]; + + // Copy the input to the state. + state[0][0] = in[0]; + state[1][0] = in[1]; + state[2][0] = in[2]; + state[3][0] = in[3]; + state[0][1] = in[4]; + state[1][1] = in[5]; + state[2][1] = in[6]; + state[3][1] = in[7]; + state[0][2] = in[8]; + state[1][2] = in[9]; + state[2][2] = in[10]; + state[3][2] = in[11]; + state[0][3] = in[12]; + state[1][3] = in[13]; + state[2][3] = in[14]; + state[3][3] = in[15]; + + // Perform the necessary number of rounds. The round key is added first. + // The last round does not perform the MixColumns step. + if (keysize > 128) { + if (keysize > 192) { + AddRoundKey(state, &key[56]); + InvShiftRows(state); + InvSubBytes(state); + AddRoundKey(state, &key[52]); + InvMixColumns(state); + InvShiftRows(state); + InvSubBytes(state); + AddRoundKey(state, &key[48]); + InvMixColumns(state); + } else { + AddRoundKey(state, &key[48]); + } + InvShiftRows(state); + InvSubBytes(state); + AddRoundKey(state, &key[44]); + InvMixColumns(state); + InvShiftRows(state); + InvSubBytes(state); + AddRoundKey(state, &key[40]); + InvMixColumns(state); + } else { + AddRoundKey(state, &key[40]); } - InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[44]);InvMixColumns(state); - InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[40]);InvMixColumns(state); - } - else { - AddRoundKey(state,&key[40]); - } - InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[36]);InvMixColumns(state); - InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[32]);InvMixColumns(state); - InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[28]);InvMixColumns(state); - InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[24]);InvMixColumns(state); - InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[20]);InvMixColumns(state); - InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[16]);InvMixColumns(state); - InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[12]);InvMixColumns(state); - InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[8]);InvMixColumns(state); - InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[4]);InvMixColumns(state); - InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[0]); - - // Copy the state to the output array. - out[0] = state[0][0]; - out[1] = state[1][0]; - out[2] = state[2][0]; - out[3] = state[3][0]; - out[4] = state[0][1]; - out[5] = state[1][1]; - out[6] = state[2][1]; - out[7] = state[3][1]; - out[8] = state[0][2]; - out[9] = state[1][2]; - out[10] = state[2][2]; - out[11] = state[3][2]; - out[12] = state[0][3]; - out[13] = state[1][3]; - out[14] = state[2][3]; - out[15] = state[3][3]; + InvShiftRows(state); + InvSubBytes(state); + AddRoundKey(state, &key[36]); + InvMixColumns(state); + InvShiftRows(state); + InvSubBytes(state); + AddRoundKey(state, &key[32]); + InvMixColumns(state); + InvShiftRows(state); + InvSubBytes(state); + AddRoundKey(state, &key[28]); + InvMixColumns(state); + InvShiftRows(state); + InvSubBytes(state); + AddRoundKey(state, &key[24]); + InvMixColumns(state); + InvShiftRows(state); + InvSubBytes(state); + AddRoundKey(state, &key[20]); + InvMixColumns(state); + InvShiftRows(state); + InvSubBytes(state); + AddRoundKey(state, &key[16]); + InvMixColumns(state); + InvShiftRows(state); + InvSubBytes(state); + AddRoundKey(state, &key[12]); + InvMixColumns(state); + InvShiftRows(state); + InvSubBytes(state); + AddRoundKey(state, &key[8]); + InvMixColumns(state); + InvShiftRows(state); + InvSubBytes(state); + AddRoundKey(state, &key[4]); + InvMixColumns(state); + InvShiftRows(state); + InvSubBytes(state); + AddRoundKey(state, &key[0]); + + // Copy the state to the output array. + out[0] = state[0][0]; + out[1] = state[1][0]; + out[2] = state[2][0]; + out[3] = state[3][0]; + out[4] = state[0][1]; + out[5] = state[1][1]; + out[6] = state[2][1]; + out[7] = state[3][1]; + out[8] = state[0][2]; + out[9] = state[1][2]; + out[10] = state[2][2]; + out[11] = state[3][2]; + out[12] = state[0][3]; + out[13] = state[1][3]; + out[14] = state[2][3]; + out[15] = state[3][3]; } - + /******************* ** AES DEBUGGING FUNCTIONS *******************/ @@ -1125,18 +1192,18 @@ void aes_decrypt(const BYTE in[], BYTE out[], const WORD key[], int keysize) void print_state(BYTE state[][4]) { int idx,idx2; - + for (idx=0; idx < 4; idx++) for (idx2=0; idx2 < 4; idx2++) printf("%02x",state[idx2][idx]); printf("\n"); } - + // This prints the key (4 consecutive ints) used for a given round as a linear hex string. void print_rnd_key(WORD key[]) { int idx; - + for (idx=0; idx < 4; idx++) printf("%08x",key[idx]); printf("\n"); diff --git a/examples/aes_encrypt/aes.h b/examples/aes_encrypt/aes.h index 9a697a293..4d4df3ab6 100644 --- a/examples/aes_encrypt/aes.h +++ b/examples/aes_encrypt/aes.h @@ -5,9 +5,9 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside - * 2016-2017 Hewlett Packard Enterprise Development LP + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside + * 2016-2019 Hewlett Packard Enterprise Development LP * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -45,127 +45,141 @@ * Details: Defines the API for the corresponding AES implementation. * Source: https://github.com/B-Con/crypto-algorithms *********************************************************************/ - -#ifndef AES_H -#define AES_H - + +#ifndef _AES_H_ +#define _AES_H_ + /*************************** HEADER FILES ***************************/ #include - + /****************************** MACROS ******************************/ -#define AES_BLOCK_SIZE 16 // AES operates on 16 bytes at a time - +#define AES_BLOCK_SIZE 16 // AES operates on 16 bytes at a time + /**************************** DATA TYPES ****************************/ -typedef unsigned char BYTE; // 8-bit byte -typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines - +typedef unsigned char BYTE; // 8-bit byte +typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines + /*********************** FUNCTION DECLARATIONS **********************/ /////////////////// // AES /////////////////// // Key setup must be done before any AES en/de-cryption functions can be used. -void aes_key_setup(const BYTE key[], // The key, must be 128, 192, or 256 bits - WORD w[], // Output key schedule to be used later - int keysize); // Bit length of the key, 128, 192, or 256 - -void aes_encrypt(const BYTE in[], // 16 bytes of plaintext - BYTE out[], // 16 bytes of ciphertext - const WORD key[], // From the key setup - int keysize); // Bit length of the key, 128, 192, or 256 - -void aes_decrypt(const BYTE in[], // 16 bytes of ciphertext - BYTE out[], // 16 bytes of plaintext - const WORD key[], // From the key setup - int keysize); // Bit length of the key, 128, 192, or 256 - +void +aes_key_setup(const BYTE key[], // The key, must be 128, 192, or 256 bits + WORD w[], // Output key schedule to be used later + int keysize); // Bit length of the key, 128, 192, or 256 + +void +aes_encrypt(const BYTE in[], // 16 bytes of plaintext + BYTE out[], // 16 bytes of ciphertext + const WORD key[], // From the key setup + int keysize); // Bit length of the key, 128, 192, or 256 + +void +aes_decrypt(const BYTE in[], // 16 bytes of ciphertext + BYTE out[], // 16 bytes of plaintext + const WORD key[], // From the key setup + int keysize); // Bit length of the key, 128, 192, or 256 + /////////////////// // AES - CBC /////////////////// -int aes_encrypt_cbc(const BYTE in[], // Plaintext - size_t in_len, // Must be a multiple of AES_BLOCK_SIZE - BYTE out[], // Ciphertext, same length as plaintext - const WORD key[], // From the key setup - int keysize, // Bit length of the key, 128, 192, or 256 - const BYTE iv[]); // IV, must be AES_BLOCK_SIZE bytes long - +int +aes_encrypt_cbc(const BYTE in[], // Plaintext + size_t in_len, // Must be a multiple of AES_BLOCK_SIZE + BYTE out[], // Ciphertext, same length as plaintext + const WORD key[], // From the key setup + int keysize, // Bit length of the key, 128, 192, or 256 + const BYTE iv[]); // IV, must be AES_BLOCK_SIZE bytes long + // Only output the CBC-MAC of the input. -int aes_encrypt_cbc_mac(const BYTE in[], // plaintext - size_t in_len, // Must be a multiple of AES_BLOCK_SIZE - BYTE out[], // Output MAC - const WORD key[], // From the key setup - int keysize, // Bit length of the key, 128, 192, or 256 - const BYTE iv[]); // IV, must be AES_BLOCK_SIZE bytes long +int +aes_encrypt_cbc_mac(const BYTE in[], // plaintext + size_t in_len, // Must be a multiple of AES_BLOCK_SIZE + BYTE out[], // Output MAC + const WORD key[], // From the key setup + int keysize, // Bit length of the key, 128, 192, or 256 + const BYTE iv[]); // IV, must be AES_BLOCK_SIZE bytes long // Only output the CBC-MAC of the input. -int aes_decrypt_cbc(const BYTE in[], // plaintext - size_t in_len, // Must be a multiple of AES_BLOCK_SIZE - BYTE out[], // Output MAC - const WORD key[], // From the key setup - int keysize, // Bit length of the key, 128, 192, or 256 - const BYTE iv[]); // IV, must be AES_BLOCK_SIZE bytes long - +int +aes_decrypt_cbc(const BYTE in[], // plaintext + size_t in_len, // Must be a multiple of AES_BLOCK_SIZE + BYTE out[], // Output MAC + const WORD key[], // From the key setup + int keysize, // Bit length of the key, 128, 192, or 256 + const BYTE iv[]); // IV, must be AES_BLOCK_SIZE bytes long + /////////////////// // AES - CTR /////////////////// -void increment_iv(BYTE iv[], // Must be a multiple of AES_BLOCK_SIZE - int counter_size); // Bytes of the IV used for counting (low end) - -void aes_encrypt_ctr(const BYTE in[], // Plaintext - size_t in_len, // Any byte length - BYTE out[], // Ciphertext, same length as plaintext - const WORD key[], // From the key setup - int keysize, // Bit length of the key, 128, 192, or 256 - const BYTE iv[]); // IV, must be AES_BLOCK_SIZE bytes long - -void aes_decrypt_ctr(const BYTE in[], // Ciphertext - size_t in_len, // Any byte length - BYTE out[], // Plaintext, same length as ciphertext - const WORD key[], // From the key setup - int keysize, // Bit length of the key, 128, 192, or 256 - const BYTE iv[]); // IV, must be AES_BLOCK_SIZE bytes long - +void +increment_iv(BYTE iv[], // Must be a multiple of AES_BLOCK_SIZE + int counter_size); // Bytes of the IV used for counting (low end) + +void +aes_encrypt_ctr(const BYTE in[], // Plaintext + size_t in_len, // Any byte length + BYTE out[], // Ciphertext, same length as plaintext + const WORD key[], // From the key setup + int keysize, // Bit length of the key, 128, 192, or 256 + const BYTE iv[]); // IV, must be AES_BLOCK_SIZE bytes long + +void +aes_decrypt_ctr(const BYTE in[], // Ciphertext + size_t in_len, // Any byte length + BYTE out[], // Plaintext, same length as ciphertext + const WORD key[], // From the key setup + int keysize, // Bit length of the key, 128, 192, or 256 + const BYTE iv[]); // IV, must be AES_BLOCK_SIZE bytes long + /////////////////// // AES - CCM /////////////////// // Returns True if the input parameters do not violate any constraint. -int aes_encrypt_ccm(const BYTE plaintext[], // IN - Plaintext. - WORD plaintext_len, // IN - Plaintext length. - const BYTE associated_data[], // IN - Associated Data included in authentication, but not encryption. - unsigned short associated_data_len, // IN - Associated Data length in bytes. - const BYTE nonce[], // IN - The Nonce to be used for encryption. - unsigned short nonce_len, // IN - Nonce length in bytes. - BYTE ciphertext[], // OUT - Ciphertext, a concatination of the plaintext and the MAC. - WORD *ciphertext_len, // OUT - The length of the ciphertext, always plaintext_len + mac_len. - WORD mac_len, // IN - The desired length of the MAC, must be 4, 6, 8, 10, 12, 14, or 16. - const BYTE key[], // IN - The AES key for encryption. - int keysize); // IN - The length of the key in bits. Valid values are 128, 192, 256. - +int +aes_encrypt_ccm(const BYTE plaintext[], // IN - Plaintext. + WORD plaintext_len, // IN - Plaintext length. + const BYTE associated_data[], // IN - Associated Data included in authentication, but not encryption. + unsigned short associated_data_len, // IN - Associated Data length in bytes. + const BYTE nonce[], // IN - The Nonce to be used for encryption. + unsigned short nonce_len, // IN - Nonce length in bytes. + BYTE ciphertext[], // OUT - Ciphertext, a concatination of the plaintext and the MAC. + WORD *ciphertext_len, // OUT - The length of the ciphertext, always plaintext_len + mac_len. + WORD mac_len, // IN - The desired length of the MAC, must be 4, 6, 8, 10, 12, 14, or 16. + const BYTE key[], // IN - The AES key for encryption. + int keysize); // IN - The length of the key in bits. Valid values are 128, 192, 256. + // Returns True if the input parameters do not violate any constraint. // Use mac_auth to ensure decryption/validation was preformed correctly. // If authentication does not succeed, the plaintext is zeroed out. To overwride // this, call with mac_auth = NULL. The proper proceedure is to decrypt with // authentication enabled (mac_auth != NULL) and make a second call to that // ignores authentication explicitly if the first call failes. -int aes_decrypt_ccm(const BYTE ciphertext[], // IN - Ciphertext, the concatination of encrypted plaintext and MAC. - WORD ciphertext_len, // IN - Ciphertext length in bytes. - const BYTE assoc[], // IN - The Associated Data, required for authentication. - unsigned short assoc_len, // IN - Associated Data length in bytes. - const BYTE nonce[], // IN - The Nonce to use for decryption, same one as for encryption. - unsigned short nonce_len, // IN - Nonce length in bytes. - BYTE plaintext[], // OUT - The plaintext that was decrypted. Will need to be large enough to hold ciphertext_len - mac_len. - WORD *plaintext_len, // OUT - Length in bytes of the output plaintext, always ciphertext_len - mac_len . - WORD mac_len, // IN - The length of the MAC that was calculated. - int *mac_auth, // OUT - TRUE if authentication succeeded, FALSE if it did not. NULL pointer will ignore the authentication. - const BYTE key[], // IN - The AES key for decryption. - int keysize); // IN - The length of the key in BITS. Valid values are 128, 192, 256. - +int +aes_decrypt_ccm( + const BYTE ciphertext[], // IN - Ciphertext, the concatination of encrypted plaintext and MAC. + WORD ciphertext_len, // IN - Ciphertext length in bytes. + const BYTE assoc[], // IN - The Associated Data, required for authentication. + unsigned short assoc_len, // IN - Associated Data length in bytes. + const BYTE nonce[], // IN - The Nonce to use for decryption, same one as for encryption. + unsigned short nonce_len, // IN - Nonce length in bytes. + BYTE plaintext[], // OUT - The plaintext that was decrypted. Will need to be large enough to hold ciphertext_len - + // mac_len. + WORD *plaintext_len, // OUT - Length in bytes of the output plaintext, always ciphertext_len - mac_len . + WORD mac_len, // IN - The length of the MAC that was calculated. + int *mac_auth, // OUT - TRUE if authentication succeeded, FALSE if it did not. NULL pointer will ignore the + // authentication. + const BYTE key[], // IN - The AES key for decryption. + int keysize); // IN - The length of the key in BITS. Valid values are 128, 192, 256. + /////////////////// // Test functions /////////////////// -//int aes_test(); -//int aes_ecb_test(); -//int aes_cbc_test(); -//int aes_ctr_test(); -//int aes_ccm_test(); - -#endif // AES_H +// int aes_test(); +// int aes_ecb_test(); +// int aes_cbc_test(); +// int aes_ctr_test(); +// int aes_ccm_test(); + +#endif // _AES_H_ diff --git a/examples/aes_encrypt/aesencrypt.c b/examples/aes_encrypt/aesencrypt.c index b84d27835..653977679 100644 --- a/examples/aes_encrypt/aesencrypt.c +++ b/examples/aes_encrypt/aesencrypt.c @@ -5,9 +5,9 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside - * 2016-2017 Hewlett Packard Enterprise Development LP + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside + * 2016-2019 Hewlett Packard Enterprise Development LP * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -39,46 +39,39 @@ * aesencrypt.c - Encrypt UDP packets using AES ********************************************************************/ -#include -#include -#include +#include +#include #include #include -#include -#include +#include +#include #include -#include #include +#include +#include #include -#include +#include #include +#include #include -#include +#include "aes.h" #include "onvm_nflib.h" #include "onvm_pkt_helper.h" -#include "aes.h" #define NF_TAG "aes_encrypt" -/* Struct that contains information about this NF */ -struct onvm_nf_info *nf_info; - /* number of package between each print */ static uint32_t print_delay = 1000000; - static uint32_t destination; /* AES encryption parameters */ -BYTE key[1][32] = { - {0x60,0x3d,0xeb,0x10,0x15,0xca,0x71,0xbe,0x2b,0x73,0xae,0xf0,0x85,0x7d,0x77,0x81,0x1f,0x35,0x2c,0x07,0x3b,0x61,0x08,0xd7,0x2d,0x98,0x10,0xa3,0x09,0x14,0xdf,0xf4} -}; -BYTE iv[1][16] = { - {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f} -}; -WORD key_schedule[60]; //word Schedule +BYTE key[1][32] = {{0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, + 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4}}; +BYTE iv[1][16] = {{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}}; +WORD key_schedule[60]; // word Schedule /* * Print a usage message @@ -102,27 +95,27 @@ parse_app_args(int argc, char *argv[], const char *progname) { while ((c = getopt(argc, argv, "d:p:")) != -1) { switch (c) { - case 'd': - destination = strtoul(optarg, NULL, 10); - dst_flag = 1; - break; - case 'p': - print_delay = strtoul(optarg, NULL, 10); - break; - case '?': - usage(progname); - if (optopt == 'd') - RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); - else if (optopt == 'p') - RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); - else if (isprint(optopt)) - RTE_LOG(INFO, APP, "Unknown option `-%c'.\n", optopt); - else - RTE_LOG(INFO, APP, "Unknown option character `\\x%x'.\n", optopt); - return -1; - default: - usage(progname); - return -1; + case 'd': + destination = strtoul(optarg, NULL, 10); + dst_flag = 1; + break; + case 'p': + print_delay = strtoul(optarg, NULL, 10); + break; + case '?': + usage(progname); + if (optopt == 'd') + RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); + else if (optopt == 'p') + RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); + else if (isprint(optopt)) + RTE_LOG(INFO, APP, "Unknown option `-%c'.\n", optopt); + else + RTE_LOG(INFO, APP, "Unknown option character `\\x%x'.\n", optopt); + return -1; + default: + usage(progname); + return -1; } } @@ -141,11 +134,11 @@ parse_app_args(int argc, char *argv[], const char *progname) { * than one lcore enabled. */ static void -do_stats_display(struct rte_mbuf* pkt) { - const char clr[] = { 27, '[', '2', 'J', '\0' }; - const char topLeft[] = { 27, '[', '1', ';', '1', 'H', '\0' }; +do_stats_display(struct rte_mbuf *pkt) { + const char clr[] = {27, '[', '2', 'J', '\0'}; + const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0'}; static uint64_t pkt_process = 0; - struct ipv4_hdr* ip; + struct ipv4_hdr *ip; pkt_process += print_delay; @@ -156,29 +149,30 @@ do_stats_display(struct rte_mbuf* pkt) { printf("-----\n"); printf("Port : %d\n", pkt->port); printf("Size : %d\n", pkt->pkt_len); - printf("N° : %"PRIu64"\n", pkt_process); + printf("N° : %" PRIu64 "\n", pkt_process); printf("\n\n"); ip = onvm_pkt_ipv4_hdr(pkt); if (ip != NULL) { - struct udp_hdr *udp; + struct udp_hdr *udp; onvm_pkt_print(pkt); - /* Check if we have a valid UDP packet */ - udp = onvm_pkt_udp_hdr(pkt); - if (udp != NULL) { - uint8_t * pkt_data; - pkt_data = ((uint8_t *) udp) + sizeof(struct udp_hdr); - printf("Payload : %.32s\n", pkt_data); - } + /* Check if we have a valid UDP packet */ + udp = onvm_pkt_udp_hdr(pkt); + if (udp != NULL) { + uint8_t *pkt_data; + pkt_data = ((uint8_t *)udp) + sizeof(struct udp_hdr); + printf("Payload : %.32s\n", pkt_data); + } } else { printf("No IP4 header found\n"); } } static int -packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, __attribute__((unused)) struct onvm_nf_info *nf_info) { - struct udp_hdr *udp; +packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, + __attribute__((unused)) struct onvm_nf_local_ctx *nf_local_ctx) { + struct udp_hdr *udp; static uint32_t counter = 0; if (++counter == print_delay) { @@ -186,56 +180,74 @@ packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, __attribute__(( counter = 0; } - /* Check if we have a valid UDP packet */ - udp = onvm_pkt_udp_hdr(pkt); - if (udp != NULL) { - uint8_t * pkt_data; - uint8_t * eth; - uint16_t plen; - uint16_t hlen; - - /* Get at the payload */ - pkt_data = ((uint8_t *) udp) + sizeof(struct udp_hdr); - /* Calculate length */ - eth = rte_pktmbuf_mtod(pkt, uint8_t *); - hlen = pkt_data - eth; - plen = pkt->pkt_len - hlen; - - /* Encrypt. */ - /* IV should change with every packet, but we don't have any - * way to send it to the other side. */ - aes_encrypt_ctr(pkt_data, plen, pkt_data, key_schedule, 256, iv[0]); - if (counter == 0) { - printf("Encrypted %d bytes at offset %d (%ld)\n", - plen, hlen, sizeof(struct ether_hdr) + sizeof(struct ipv4_hdr) + sizeof(struct udp_hdr)); - } - } + /* Check if we have a valid UDP packet */ + udp = onvm_pkt_udp_hdr(pkt); + if (udp != NULL) { + uint8_t *pkt_data; + uint8_t *eth; + uint16_t plen; + uint16_t hlen; + + /* Get at the payload */ + pkt_data = ((uint8_t *)udp) + sizeof(struct udp_hdr); + /* Calculate length */ + eth = rte_pktmbuf_mtod(pkt, uint8_t *); + hlen = pkt_data - eth; + plen = pkt->pkt_len - hlen; + + /* Encrypt. */ + /* IV should change with every packet, but we don't have any + * way to send it to the other side. */ + aes_encrypt_ctr(pkt_data, plen, pkt_data, key_schedule, 256, iv[0]); + if (counter == 0) { + printf("Encrypted %d bytes at offset %d (%ld)\n", plen, hlen, + sizeof(struct ether_hdr) + sizeof(struct ipv4_hdr) + sizeof(struct udp_hdr)); + } + } meta->action = ONVM_NF_ACTION_TONF; meta->destination = destination; return 0; } - -int main(int argc, char *argv[]) { +int +main(int argc, char *argv[]) { + struct onvm_nf_local_ctx *nf_local_ctx; + struct onvm_nf_function_table *nf_function_table; int arg_offset; const char *progname = argv[0]; - if ((arg_offset = onvm_nflib_init(argc, argv, NF_TAG, &nf_info)) < 0) - return -1; + nf_local_ctx = onvm_nflib_init_nf_local_ctx(); + onvm_nflib_start_signal_handler(nf_local_ctx, NULL); + + nf_function_table = onvm_nflib_init_nf_function_table(); + nf_function_table->pkt_handler = &packet_handler; + + if ((arg_offset = onvm_nflib_init(argc, argv, NF_TAG, nf_local_ctx, nf_function_table)) < 0) { + onvm_nflib_stop(nf_local_ctx); + if (arg_offset == ONVM_SIGNAL_TERMINATION) { + printf("Exiting due to user termination\n"); + return 0; + } else { + rte_exit(EXIT_FAILURE, "Failed ONVM init\n"); + } + } + argc -= arg_offset; argv += arg_offset; if (parse_app_args(argc, argv, progname) < 0) { - onvm_nflib_stop(nf_info); + onvm_nflib_stop(nf_local_ctx); rte_exit(EXIT_FAILURE, "Invalid command-line arguments\n"); } - /* Initialise encryption engine. Key should be configurable. */ - aes_key_setup(key[0], key_schedule, 256); + /* Initialise encryption engine. Key should be configurable. */ + aes_key_setup(key[0], key_schedule, 256); + + onvm_nflib_run(nf_local_ctx); - onvm_nflib_run(nf_info, &packet_handler); + onvm_nflib_stop(nf_local_ctx); printf("If we reach here, program is ending\n"); return 0; } diff --git a/examples/arp_response/arp_response.c b/examples/arp_response/arp_response.c index 68a030001..de74c8661 100644 --- a/examples/arp_response/arp_response.c +++ b/examples/arp_response/arp_response.c @@ -5,8 +5,8 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,24 +38,24 @@ * arp_response.c - an example using onvm. If it receives an ARP packet, send a response. ********************************************************************/ -#include -#include -#include +#include +#include #include #include -#include -#include +#include +#include #include -#include #include +#include +#include +#include #include -#include -#include #include -#include -#include +#include #include +#include +#include #include "onvm_nflib.h" #include "onvm_pkt_helper.h" @@ -71,8 +71,6 @@ struct state_info { int print_flag; }; -/* Struct that contains information about this NF */ -struct onvm_nf_info *nf_info; struct state_info *state_info; /* shared data structure containing host port info */ @@ -85,8 +83,13 @@ usage(const char *progname) { printf("%s [EAL args] -- [NF Lib args] -- -d -s [-p enable printing]\n", progname); printf("%s -F [EAL args] -- [NF_LIB args] -- [NF args]>\n\n", progname); printf("Flags:\n"); - printf(" - `-d `: the NF will send non-ARP packets to the NF at this service ID, e.g. `-d 2` sends packets to service ID 2\n"); - printf(" - `-s `: the NF will map each comma separated IP (no spaces) to the corresponding port. Example: `-s 10.0.0.31,11.0.0.31` maps port 0 to 10.0.0.31, and port 1 to 11.0.0.31. If 0.0.0.0 is inputted, the IP will be 0. If too few IPs are inputted, the remaining ports will be ignored.\n"); + printf( + " - `-d `: the NF will send non-ARP packets to the NF at this service ID, e.g. `-d 2` " + "sends packets to service ID 2\n"); + printf( + " - `-s `: the NF will map each comma separated IP (no spaces) to the corresponding port. " + "Example: `-s 10.0.0.31,11.0.0.31` maps port 0 to 10.0.0.31, and port 1 to 11.0.0.31. If 0.0.0.0 is " + "inputted, the IP will be 0. If too few IPs are inputted, the remaining ports will be ignored.\n"); printf(" - `-p`: Enables printing of log information\n"); } @@ -141,53 +144,53 @@ parse_app_args(int argc, char *argv[], const char *progname) { } while ((c = getopt(argc, argv, "d:s:p")) != -1) { - switch(c) { - case 'd': - state_info->nf_destination = strtoul(optarg, NULL, 10); - dst_flag = 1; - RTE_LOG(INFO, APP, "Sending packets to service ID %d\n", state_info->nf_destination); - break; - case 's': - num_ips = get_ip_count(optarg, delim); - if (num_ips > ports->num_ports) { - RTE_LOG(INFO, APP, "Too many IPs were entered!\n"); - return -1; - } - - if (num_ips < 0) { - RTE_LOG(INFO, APP, "Invalid IP pointer\n"); - return -1; - } + switch (c) { + case 'd': + state_info->nf_destination = strtoul(optarg, NULL, 10); + dst_flag = 1; + RTE_LOG(INFO, APP, "Sending packets to service ID %d\n", state_info->nf_destination); + break; + case 's': + num_ips = get_ip_count(optarg, delim); + if (num_ips > ports->num_ports) { + RTE_LOG(INFO, APP, "Too many IPs were entered!\n"); + return -1; + } - token = strtok_r(optarg, delim, &buffer); - while (token != NULL) { - result = onvm_pkt_parse_ip(token, &state_info->source_ips[current_ip]); - if (result < 0) { - RTE_LOG(INFO, APP, "Invalid IP entered"); + if (num_ips < 0) { + RTE_LOG(INFO, APP, "Invalid IP pointer\n"); return -1; } - ++current_ip; - token = strtok_r(NULL, delim, &buffer); - } - - ip_flag = 1; - break; - case 'p': - state_info->print_flag = 1; - break; - case '?': - usage(progname); - if (optopt == 'd') - RTE_LOG(INFO, APP, "Option -%c requires an argument\n", optopt); - else if (optopt == 'd') - RTE_LOG(INFO, APP, "Option -%c requires an argument\n", optopt); - else - RTE_LOG(INFO, APP, "Unknown option character\n"); - - return -1; - default: - usage(progname); - return -1; + + token = strtok_r(optarg, delim, &buffer); + while (token != NULL) { + result = onvm_pkt_parse_ip(token, &state_info->source_ips[current_ip]); + if (result < 0) { + RTE_LOG(INFO, APP, "Invalid IP entered"); + return -1; + } + ++current_ip; + token = strtok_r(NULL, delim, &buffer); + } + + ip_flag = 1; + break; + case 'p': + state_info->print_flag = 1; + break; + case '?': + usage(progname); + if (optopt == 'd') + RTE_LOG(INFO, APP, "Option -%c requires an argument\n", optopt); + else if (optopt == 'd') + RTE_LOG(INFO, APP, "Option -%c requires an argument\n", optopt); + else + RTE_LOG(INFO, APP, "Unknown option character\n"); + + return -1; + default: + usage(progname); + return -1; } } @@ -208,7 +211,7 @@ parse_app_args(int argc, char *argv[], const char *progname) { * For RFC about ARP, see https://tools.ietf.org/html/rfc826 * RETURNS 0 if success, -1 otherwise */ static int -send_arp_reply(int port, struct ether_addr *tha, uint32_t tip) { +send_arp_reply(int port, struct ether_addr *tha, uint32_t tip, struct onvm_nf *nf) { struct rte_mbuf *out_pkt = NULL; struct onvm_pkt_meta *pmeta = NULL; struct ether_hdr *eth_hdr = NULL; @@ -230,13 +233,13 @@ send_arp_reply(int port, struct ether_addr *tha, uint32_t tip) { out_pkt->data_len = pkt_size; out_pkt->pkt_len = pkt_size; - //SET ETHER HEADER INFO + // SET ETHER HEADER INFO eth_hdr = onvm_pkt_ether_hdr(out_pkt); ether_addr_copy(&ports->mac[port], ð_hdr->s_addr); eth_hdr->ether_type = rte_cpu_to_be_16(ETHER_TYPE_ARP); ether_addr_copy(tha, ð_hdr->d_addr); - //SET ARP HDR INFO + // SET ARP HDR INFO out_arp_hdr = rte_pktmbuf_mtod_offset(out_pkt, struct arp_hdr *, sizeof(struct ether_hdr)); out_arp_hdr->arp_hrd = rte_cpu_to_be_16(ARP_HRD_ETHER); @@ -251,16 +254,17 @@ send_arp_reply(int port, struct ether_addr *tha, uint32_t tip) { out_arp_hdr->arp_data.arp_tip = tip; ether_addr_copy(tha, &out_arp_hdr->arp_data.arp_tha); - //SEND PACKET OUT/SET METAINFO + // SEND PACKET OUT/SET METAINFO pmeta = onvm_get_pkt_meta(out_pkt); pmeta->destination = port; pmeta->action = ONVM_NF_ACTION_OUT; - return onvm_nflib_return_pkt(nf_info, out_pkt); + return onvm_nflib_return_pkt(nf, out_pkt); } static int -packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, __attribute__((unused)) struct onvm_nf_info *nf_info) { +packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, + __attribute__((unused)) struct onvm_nf_local_ctx *nf_local_ctx) { struct ether_hdr *eth_hdr = onvm_pkt_ether_hdr(pkt); struct arp_hdr *in_arp_hdr = NULL; int result = -1; @@ -276,10 +280,13 @@ packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, __attribute__(( in_arp_hdr = rte_pktmbuf_mtod_offset(pkt, struct arp_hdr *, sizeof(struct ether_hdr)); switch (rte_cpu_to_be_16(in_arp_hdr->arp_op)) { case ARP_OP_REQUEST: - if (in_arp_hdr->arp_data.arp_tip == state_info->source_ips[ports->id[pkt->port]]) { - result = send_arp_reply(pkt->port, ð_hdr->s_addr, in_arp_hdr->arp_data.arp_sip); + if (rte_be_to_cpu_32(in_arp_hdr->arp_data.arp_tip) == + state_info->source_ips[ports->id[pkt->port]]) { + result = send_arp_reply(pkt->port, ð_hdr->s_addr, + in_arp_hdr->arp_data.arp_sip, nf_local_ctx->nf); if (state_info->print_flag) { - printf("ARP Reply From Port %d (ID %d): %d\n", pkt->port, ports->id[pkt->port], result); + printf("ARP Reply From Port %d (ID %d): %d\n", pkt->port, + ports->id[pkt->port], result); } meta->action = ONVM_NF_ACTION_DROP; return 0; @@ -290,7 +297,8 @@ packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, __attribute__(( break; default: if (state_info->print_flag) { - printf("ARP with opcode %d, port %d (ID %d) DROPPED\n", rte_cpu_to_be_16(in_arp_hdr->arp_op),pkt->port, ports->id[pkt->port]); + printf("ARP with opcode %d, port %d (ID %d) DROPPED\n", + rte_cpu_to_be_16(in_arp_hdr->arp_op), pkt->port, ports->id[pkt->port]); } } } @@ -300,12 +308,27 @@ packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, __attribute__(( return 0; } -int main(int argc, char *argv[]) { +int +main(int argc, char *argv[]) { int arg_offset; + struct onvm_nf_local_ctx *nf_local_ctx; + struct onvm_nf_function_table *nf_function_table; const char *progname = argv[0]; - if ((arg_offset = onvm_nflib_init(argc, argv, NF_TAG, &nf_info)) < 0) { - return -1; + nf_local_ctx = onvm_nflib_init_nf_local_ctx(); + onvm_nflib_start_signal_handler(nf_local_ctx, NULL); + + nf_function_table = onvm_nflib_init_nf_function_table(); + nf_function_table->pkt_handler = &packet_handler; + + if ((arg_offset = onvm_nflib_init(argc, argv, NF_TAG, nf_local_ctx, nf_function_table)) < 0) { + onvm_nflib_stop(nf_local_ctx); + if (arg_offset == ONVM_SIGNAL_TERMINATION) { + printf("Exiting due to user termination\n"); + return 0; + } else { + rte_exit(EXIT_FAILURE, "Failed ONVM init\n"); + } } argc -= arg_offset; @@ -313,22 +336,24 @@ int main(int argc, char *argv[]) { state_info = rte_calloc("state", 1, sizeof(struct state_info), 0); if (state_info == NULL) { - onvm_nflib_stop(nf_info); + onvm_nflib_stop(nf_local_ctx); rte_exit(EXIT_FAILURE, "Unable to initialize NF state"); } state_info->pktmbuf_pool = rte_mempool_lookup(PKTMBUF_POOL_NAME); if (state_info->pktmbuf_pool == NULL) { - onvm_nflib_stop(nf_info); + onvm_nflib_stop(nf_local_ctx); rte_exit(EXIT_FAILURE, "Cannot find mbuf pool!\n"); } if (parse_app_args(argc, argv, progname) < 0) { - onvm_nflib_stop(nf_info); + onvm_nflib_stop(nf_local_ctx); rte_exit(EXIT_FAILURE, "Invalid command-line arguments"); } - onvm_nflib_run(nf_info, &packet_handler); + onvm_nflib_run(nf_local_ctx); + + onvm_nflib_stop(nf_local_ctx); printf("If we reach here, program is ending\n"); return 0; } diff --git a/examples/basic_monitor/monitor.c b/examples/basic_monitor/monitor.c index 4fa51af03..1c2ec33f6 100644 --- a/examples/basic_monitor/monitor.c +++ b/examples/basic_monitor/monitor.c @@ -5,8 +5,8 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,30 +38,27 @@ * monitor.c - an example using onvm. Print a message each p package received ********************************************************************/ -#include -#include -#include +#include +#include #include #include -#include -#include +#include +#include #include -#include #include +#include +#include #include -#include -#include #include +#include +#include #include "onvm_nflib.h" #include "onvm_pkt_helper.h" #define NF_TAG "basic_monitor" -/* Struct that contains information about this NF */ -struct onvm_nf_info *nf_info; - /* number of package between each print */ static uint32_t print_delay = 1000000; @@ -79,7 +76,7 @@ static void usage(const char *progname) { printf("Usage:\n"); printf("%s [EAL args] -- [NF_LIB args] -- -p \n", progname); - printf("%s -F [EAL args] -- [NF_LIB args] -- [NF args]\n\n", progname); + printf("%s -F [EAL args] -- [NF_LIB args] -- [NF args]\n\n", progname); printf("Flags:\n"); printf(" - `-p `: number of packets between each print, e.g. `-p 1` prints every packets.\n"); } @@ -91,24 +88,24 @@ static int parse_app_args(int argc, char *argv[], const char *progname) { int c; - while ((c = getopt (argc, argv, "p:")) != -1) { + while ((c = getopt(argc, argv, "p:")) != -1) { switch (c) { - case 'p': - print_delay = strtoul(optarg, NULL, 10); - RTE_LOG(INFO, APP, "print_delay = %d\n", print_delay); - break; - case '?': - usage(progname); - if (optopt == 'p') - RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); - else if (isprint(optopt)) - RTE_LOG(INFO, APP, "Unknown option `-%c'.\n", optopt); - else - RTE_LOG(INFO, APP, "Unknown option character `\\x%x'.\n", optopt); - return -1; - default: - usage(progname); - return -1; + case 'p': + print_delay = strtoul(optarg, NULL, 10); + RTE_LOG(INFO, APP, "print_delay = %d\n", print_delay); + break; + case '?': + usage(progname); + if (optopt == 'p') + RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); + else if (isprint(optopt)) + RTE_LOG(INFO, APP, "Unknown option `-%c'.\n", optopt); + else + RTE_LOG(INFO, APP, "Unknown option character `\\x%x'.\n", optopt); + return -1; + default: + usage(progname); + return -1; } } return optind; @@ -121,11 +118,11 @@ parse_app_args(int argc, char *argv[], const char *progname) { * than one lcore enabled. */ static void -do_stats_display(struct rte_mbuf* pkt) { - const char clr[] = { 27, '[', '2', 'J', '\0' }; - const char topLeft[] = { 27, '[', '1', ';', '1', 'H', '\0' }; +do_stats_display(struct rte_mbuf *pkt) { + const char clr[] = {27, '[', '2', 'J', '\0'}; + const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0'}; static uint64_t pkt_process = 0; - struct ipv4_hdr* ip; + struct ipv4_hdr *ip; pkt_process += print_delay; @@ -137,7 +134,7 @@ do_stats_display(struct rte_mbuf* pkt) { printf("Port : %d\n", pkt->port); printf("Size : %d\n", pkt->pkt_len); printf("Hash : %u\n", pkt->hash.rss); - printf("N° : %"PRIu64"\n", pkt_process); + printf("N° : %" PRIu64 "\n", pkt_process); printf("\n\n"); ip = onvm_pkt_ipv4_hdr(pkt); @@ -149,7 +146,7 @@ do_stats_display(struct rte_mbuf* pkt) { } static int -callback_handler(__attribute__((unused)) struct onvm_nf_info *nf_info) { +callback_handler(__attribute__((unused)) struct onvm_nf_local_ctx *nf_local_ctx) { cur_cycles = rte_get_tsc_cycles(); if (((cur_cycles - last_cycle) / rte_get_timer_hz()) > 5) { @@ -161,7 +158,8 @@ callback_handler(__attribute__((unused)) struct onvm_nf_info *nf_info) { } static int -packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, __attribute__((unused)) struct onvm_nf_info *nf_info) { +packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, + __attribute__((unused)) struct onvm_nf_local_ctx *nf_local_ctx) { static uint32_t counter = 0; total_packets++; if (++counter == print_delay) { @@ -178,26 +176,44 @@ packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, __attribute__(( return 0; } - -int main(int argc, char *argv[]) { +int +main(int argc, char *argv[]) { + struct onvm_nf_local_ctx *nf_local_ctx; + struct onvm_nf_function_table *nf_function_table; int arg_offset; const char *progname = argv[0]; - if ((arg_offset = onvm_nflib_init(argc, argv, NF_TAG, &nf_info)) < 0) - return -1; + nf_local_ctx = onvm_nflib_init_nf_local_ctx(); + onvm_nflib_start_signal_handler(nf_local_ctx, NULL); + + nf_function_table = onvm_nflib_init_nf_function_table(); + nf_function_table->pkt_handler = &packet_handler; + nf_function_table->user_actions = &callback_handler; + + if ((arg_offset = onvm_nflib_init(argc, argv, NF_TAG, nf_local_ctx, nf_function_table)) < 0) { + onvm_nflib_stop(nf_local_ctx); + if (arg_offset == ONVM_SIGNAL_TERMINATION) { + printf("Exiting due to user termination\n"); + return 0; + } else { + rte_exit(EXIT_FAILURE, "Failed ONVM init\n"); + } + } argc -= arg_offset; argv += arg_offset; if (parse_app_args(argc, argv, progname) < 0) { - onvm_nflib_stop(nf_info); + onvm_nflib_stop(nf_local_ctx); rte_exit(EXIT_FAILURE, "Invalid command-line arguments\n"); } cur_cycles = rte_get_tsc_cycles(); last_cycle = rte_get_tsc_cycles(); - onvm_nflib_run_callback(nf_info, &packet_handler, &callback_handler); + onvm_nflib_run(nf_local_ctx); + + onvm_nflib_stop(nf_local_ctx); printf("If we reach here, program is ending\n"); return 0; } diff --git a/examples/bridge/bridge.c b/examples/bridge/bridge.c index 8f76bd948..cef48a7ca 100644 --- a/examples/bridge/bridge.c +++ b/examples/bridge/bridge.c @@ -5,8 +5,8 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,29 +38,26 @@ * bridge.c - send all packets from one port out the other. ********************************************************************/ -#include -#include -#include +#include +#include #include #include -#include -#include +#include +#include #include -#include #include +#include +#include #include -#include #include +#include #include "onvm_nflib.h" #include "onvm_pkt_helper.h" #define NF_TAG "bridge" -/* Struct that contains information about this NF */ -struct onvm_nf_info *nf_info; - /* number of package between each print */ static uint32_t print_delay = 1000000; @@ -72,7 +69,7 @@ usage(const char *progname) { printf("Usage:\n"); printf("%s [EAL args] -- [NF_LIB args] -- -p \n", progname); printf("%s -F [EAL args] -- [NF_LIB args] -- [NF args]\n\n", progname); - printf("Flags:\n"); + printf("Flags:\n"); printf(" - `-p `: number of packets between each print, e.g. `-p 1` prints every packets.\n"); } @@ -83,23 +80,23 @@ static int parse_app_args(int argc, char *argv[], const char *progname) { int c; - while ((c = getopt (argc, argv, "p:")) != -1) { + while ((c = getopt(argc, argv, "p:")) != -1) { switch (c) { - case 'p': - print_delay = strtoul(optarg, NULL, 10); - break; - case '?': - usage(progname); - if (optopt == 'p') - RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); - else if (isprint(optopt)) - RTE_LOG(INFO, APP, "Unknown option `-%c'.\n", optopt); - else - RTE_LOG(INFO, APP, "Unknown option character `\\x%x'.\n", optopt); - return -1; - default: - usage(progname); - return -1; + case 'p': + print_delay = strtoul(optarg, NULL, 10); + break; + case '?': + usage(progname); + if (optopt == 'p') + RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); + else if (isprint(optopt)) + RTE_LOG(INFO, APP, "Unknown option `-%c'.\n", optopt); + else + RTE_LOG(INFO, APP, "Unknown option character `\\x%x'.\n", optopt); + return -1; + default: + usage(progname); + return -1; } } return optind; @@ -112,12 +109,12 @@ parse_app_args(int argc, char *argv[], const char *progname) { * than one lcore enabled. */ static void -do_stats_display(struct rte_mbuf* pkt) { - const char clr[] = { 27, '[', '2', 'J', '\0' }; - const char topLeft[] = { 27, '[', '1', ';', '1', 'H', '\0' }; +do_stats_display(struct rte_mbuf *pkt) { + const char clr[] = {27, '[', '2', 'J', '\0'}; + const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0'}; static uint64_t pkt_process = 0; - struct ipv4_hdr* ip; + struct ipv4_hdr *ip; pkt_process += print_delay; @@ -129,13 +126,12 @@ do_stats_display(struct rte_mbuf* pkt) { printf("Port : %d\n", pkt->port); printf("Size : %d\n", pkt->pkt_len); printf("Type : %d\n", pkt->packet_type); - printf("Number of packet processed : %"PRIu64"\n", pkt_process); + printf("Number of packet processed : %" PRIu64 "\n", pkt_process); ip = onvm_pkt_ipv4_hdr(pkt); - if(ip != NULL) { + if (ip != NULL) { onvm_pkt_print(pkt); - } - else { + } else { printf("Not IP4\n"); } @@ -143,7 +139,8 @@ do_stats_display(struct rte_mbuf* pkt) { } static int -packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, __attribute__((unused)) struct onvm_nf_info *nf_info) { +packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, + __attribute__((unused)) struct onvm_nf_local_ctx *nf_local_ctx) { static uint32_t counter = 0; if (counter++ == print_delay) { do_stats_display(pkt); @@ -152,31 +149,47 @@ packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, __attribute__(( if (pkt->port == 0) { meta->destination = 1; - } - else { + } else { meta->destination = 0; } meta->action = ONVM_NF_ACTION_OUT; return 0; } - -int main(int argc, char *argv[]) { +int +main(int argc, char *argv[]) { int arg_offset; - + struct onvm_nf_local_ctx *nf_local_ctx; + struct onvm_nf_function_table *nf_function_table; const char *progname = argv[0]; - if ((arg_offset = onvm_nflib_init(argc, argv, NF_TAG, &nf_info)) < 0) - return -1; + nf_local_ctx = onvm_nflib_init_nf_local_ctx(); + onvm_nflib_start_signal_handler(nf_local_ctx, NULL); + + nf_function_table = onvm_nflib_init_nf_function_table(); + nf_function_table->pkt_handler = &packet_handler; + + if ((arg_offset = onvm_nflib_init(argc, argv, NF_TAG, nf_local_ctx, nf_function_table)) < 0) { + onvm_nflib_stop(nf_local_ctx); + if (arg_offset == ONVM_SIGNAL_TERMINATION) { + printf("Exiting due to user termination\n"); + return 0; + } else { + rte_exit(EXIT_FAILURE, "Failed ONVM init\n"); + } + } + argc -= arg_offset; argv += arg_offset; if (parse_app_args(argc, argv, progname) < 0) { - onvm_nflib_stop(nf_info); + onvm_nflib_stop(nf_local_ctx); rte_exit(EXIT_FAILURE, "Invalid command-line arguments\n"); } - onvm_nflib_run(nf_info, &packet_handler); + onvm_nflib_run(nf_local_ctx); + + onvm_nflib_stop(nf_local_ctx); printf("If we reach here, program is ending\n"); return 0; } diff --git a/examples/firewall/.gitignore b/examples/firewall/.gitignore new file mode 100755 index 000000000..10388a2f8 --- /dev/null +++ b/examples/firewall/.gitignore @@ -0,0 +1,2 @@ +firewall/ +build/ diff --git a/examples/firewall/Makefile b/examples/firewall/Makefile new file mode 100755 index 000000000..02a6e4e28 --- /dev/null +++ b/examples/firewall/Makefile @@ -0,0 +1,62 @@ +# openNetVM +# https://github.com/sdnfv/openNetVM +# +# BSD LICENSE +# +# Copyright(c) +# 2015-2016 George Washington University +# 2015-2016 University of California Riverside +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# The name of the author may not be used to endorse or promote +# products derived from this software without specific prior +# written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +ifeq ($(RTE_SDK),) +$(error "Please define RTE_SDK environment variable") +endif + +RTE_TARGET ?= $(RTE_TARGET) + +# Default target, can be overriden by command line or environment +include $(RTE_SDK)/mk/rte.vars.mk + +# binary name +APP = firewall + +# all source are stored in SRCS-y +SRCS-y := firewall.c + +# OpenNetVM path +ONVM= $(SRCDIR)/../../onvm + +CFLAGS += -O3 $(USER_FLAGS) + +CFLAGS += -I$(ONVM)/onvm_nflib +CFLAGS += -I$(ONVM)/lib +LDFLAGS += $(ONVM)/onvm_nflib/$(RTE_TARGET)/libonvm.a +LDFLAGS += $(ONVM)/lib/$(RTE_TARGET)/lib/libonvmhelper.a -lm + +include $(RTE_SDK)/mk/rte.extapp.mk diff --git a/examples/firewall/README.md b/examples/firewall/README.md new file mode 100755 index 000000000..7e18d4a83 --- /dev/null +++ b/examples/firewall/README.md @@ -0,0 +1,31 @@ +Firewall +== +The Firewall NF drops/forwards packets based on LPM rules specified in the rules.json file. +A user would enter a rule in the following format: + +```` +"ruleName": { + "ip": "127.1.1.0", + "depth": 32, + "action": 0 + } +```` +Compilation and Execution +-- +``` +cd examples +make +cd firewall +./go.sh SERVICE_ID -d DESTINATION_ID -f RULES_FILE + +OR + +./go.sh -F CONFIG_FILE -- -- -d DST -f RULES_FILE [-p PRINT_DELAY] [-b debug mode] +``` + +App Specific Arguments +-- + - `-b`: specifies debug mode. Prints individual packet source ip addresses. + - `-f `: rules used for LPM lookup. + - `-p +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cJSON.h" + +#include +#include +#include +#include + +#include + +#include "onvm_nflib.h" +#include "onvm_pkt_helper.h" +#include "onvm_config_common.h" + +#define NF_TAG "firewall" + +#define MAX_RULES 256 +#define NUM_TBLS 8 + +static uint16_t destination; +static int debug = 0; +char *rule_file = NULL; + +/* Structs that contain information to setup LPM and its rules */ +struct lpm_request *firewall_req; +static struct firewall_pkt_stats stats; +struct rte_lpm *lpm_tbl; +struct onvm_fw_rule **rules; + +/* Number of packets between each print */ +static uint32_t print_delay = 10000000; + +/* Shared data structure containing host port info */ +extern struct port_info *ports; + +/* Struct for the firewall LPM rules */ +struct onvm_fw_rule { + uint32_t src_ip; + uint8_t depth; + uint8_t action; +}; + +/* Struct for printing stats */ +struct firewall_pkt_stats { + uint64_t pkt_drop; + uint64_t pkt_accept; + uint64_t pkt_not_ipv4; + uint64_t pkt_total; +}; + +/* + * Print a usage message + */ +static void +usage(const char *progname) { + printf("Usage: %s [EAL args] -- [NF_LIB args] -- -p -f [-b]\n\n", progname); + printf("Flags:\n"); + printf(" - `-d DST`: Destination Service ID to forward to\n"); + printf(" - `-p PRINT_DELAY`: Number of packets between each print, e.g. `-p 1` prints every packets.\n"); + printf(" - `-b`: Debug mode: Print each incoming packets source/destination" + " IP address as well as its drop/forward status\n"); + printf(" - `-f`: Path to a JSON file containing firewall rules; See README for example usage\n"); +} + +/* + * Parse the application arguments. + */ +static int +parse_app_args(int argc, char *argv[], const char *progname) { + int c, dst_flag = 0, rules_init = 0; + + while ((c = getopt(argc, argv, "d:f:p:b")) != -1) { + switch (c) { + case 'd': + destination = strtoul(optarg, NULL, 10); + dst_flag = 1; + break; + case 'p': + print_delay = strtoul(optarg, NULL, 10); + RTE_LOG(INFO, APP, "Print delay = %d\n", print_delay); + break; + case 'f': + rule_file = strdup(optarg); + rules_init = 1; + break; + case 'b': + RTE_LOG(INFO, APP, "Debug mode enabled; printing the source IP addresses" + " of each incoming packet as well as drop/forward status\n"); + debug = 1; + break; + case '?': + usage(progname); + if (optopt == 'p') + RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); + if (optopt == 'd') + RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); + if (optopt == 'f') + RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); + else if (isprint(optopt)) + RTE_LOG(INFO, APP, "Unknown option `-%c'.\n", optopt); + else + RTE_LOG(INFO, APP, "Unknown option character `\\x%x'.\n", optopt); + return -1; + default: + usage(progname); + return -1; + } + } + + if (!dst_flag) { + RTE_LOG(INFO, APP, "Firewall NF requires a destination NF with the -d flag.\n"); + return -1; + } + if (!debug) { + RTE_LOG(INFO, APP, "Running normal mode, use -b flag to enable debug mode\n"); + } + if (!rules_init) { + RTE_LOG(INFO, APP, "Please specify a rules JSON file with -f FILE_NAME\n"); + return -1; + } + return optind; +} + +/* + * This function displays stats. It uses ANSI terminal codes to clear + * screen when called. It is called from a single non-master + * thread in the server process, when the process is run with more + * than one lcore enabled. + */ +static void +do_stats_display(void) { + const char clr[] = {27, '[', '2', 'J', '\0'}; + const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0'}; + + /* Clear screen and move to top left */ + printf("%s%s", clr, topLeft); + printf("Packets Dropped: %lu\n", stats.pkt_drop); + printf("Packets not IPv4: %lu\n", stats.pkt_not_ipv4); + printf("Packets Accepted: %lu\n", stats.pkt_accept); + printf("Packets Total: %lu", stats.pkt_total); + + printf("\n\n"); +} + +static int +packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, + __attribute__((unused)) struct onvm_nf_local_ctx *nf_local_ctx) { + struct ipv4_hdr *ipv4_hdr; + static uint32_t counter = 0; + int ret; + uint32_t rule = 0; + uint32_t track_ip = 0; + char ip_string[16]; + + if (++counter == print_delay) { + do_stats_display(); + counter = 0; + } + + stats.pkt_total++; + + if (!onvm_pkt_is_ipv4(pkt)) { + if (debug) RTE_LOG(INFO, APP, "Packet received not ipv4\n"); + stats.pkt_not_ipv4++; + meta->action = ONVM_NF_ACTION_DROP; + return 0; + } + + ipv4_hdr = onvm_pkt_ipv4_hdr(pkt); + ret = rte_lpm_lookup(lpm_tbl, rte_be_to_cpu_32(ipv4_hdr->src_addr), &rule); + + if (debug) onvm_pkt_parse_char_ip(ip_string, rte_be_to_cpu_32(ipv4_hdr->src_addr)); + + if (ret < 0) { + meta->action = ONVM_NF_ACTION_DROP; + stats.pkt_drop++; + if (debug) RTE_LOG(INFO, APP, "Packet from source IP %s has been dropped\n", ip_string); + return 0; + } + + switch (rule) { + case 0: + meta->action = ONVM_NF_ACTION_TONF; + meta->destination = destination; + stats.pkt_accept++; + if (debug) RTE_LOG(INFO, APP, "Packet from source IP %s has been accepted\n", ip_string); + break; + default: + meta->action = ONVM_NF_ACTION_DROP; + stats.pkt_drop++; + if (debug) RTE_LOG(INFO, APP, "Packet from source IP %s has been dropped\n", ip_string); + break; + } + + return 0; +} + +static int +lpm_setup(struct onvm_fw_rule **rules, int num_rules) { + int i, status, ret; + uint32_t ip; + char name[64]; + char ip_string[16]; + + firewall_req = (struct lpm_request *) rte_malloc(NULL, sizeof(struct lpm_request), 0); + + if (!firewall_req) return 0; + + snprintf(name, sizeof(name), "fw%d-%"PRIu64, rte_lcore_id(), rte_get_tsc_cycles()); + firewall_req->max_num_rules = 1024; + firewall_req->num_tbl8s = 24; + firewall_req->socket_id = rte_socket_id(); + snprintf(firewall_req->name, sizeof(name), "%s", name); + status = onvm_nflib_request_lpm(firewall_req); + + if (status < 0) { + rte_exit(EXIT_FAILURE, "Cannot get lpm region for firewall\n"); + } + + lpm_tbl = rte_lpm_find_existing(name); + + if (lpm_tbl == NULL) { + printf("No existing LPM_TBL\n"); + } + + for (i = 0; i < num_rules; ++i) { + ip = rules[i]->src_ip; + onvm_pkt_parse_char_ip(ip_string, ip); + printf("RULE %d: { ip: %s, depth: %d, action: %d }\n", i, ip_string, rules[i]->depth, rules[i]->action); + ret = rte_lpm_add(lpm_tbl, rules[i]->src_ip, rules[i]->depth, rules[i]->action); + if (ret < 0) { + printf("ERROR ADDING RULE %d\n", ret); + return 1; + } + } + rte_free(firewall_req); + + return 0; +} + +static void +lpm_teardown(struct onvm_fw_rule **rules, int num_rules) { + int i; + + if (rules) { + for (i = 0; i < num_rules; ++i) { + if (rules[i]) free(rules[i]); + } + free(rules); + } + + if (lpm_tbl) { + rte_lpm_free(lpm_tbl); + } + + if (rule_file) { + free(rule_file); + } +} + +struct onvm_fw_rule +**setup_rules(int *total_rules, char *rules_file) { + int ip[4]; + int num_rules, ret; + int i = 0; + + cJSON *rules_json = onvm_config_parse_file(rules_file); + cJSON *rules_ip = NULL; + cJSON *depth = NULL; + cJSON *action = NULL; + + if (rules_json == NULL) { + rte_exit(EXIT_FAILURE, "%s file could not be parsed/not found. Assure rules file" + " the directory to the rules file is being specified.\n", rules_file); + } + + num_rules = onvm_config_get_item_count(rules_json); + *total_rules = num_rules; + rules = (struct onvm_fw_rule **) malloc(num_rules * sizeof(struct onvm_fw_rule *)); + rules_json = rules_json->child; + + while (rules_json != NULL) { + rules_ip = cJSON_GetObjectItem(rules_json, "ip"); + depth = cJSON_GetObjectItem(rules_json, "depth"); + action = cJSON_GetObjectItem(rules_json, "action"); + + if (rules_ip == NULL) rte_exit(EXIT_FAILURE, "IP not found/invalid\n"); + if (depth == NULL) rte_exit(EXIT_FAILURE, "Depth not found/invalid\n"); + if (action == NULL) rte_exit(EXIT_FAILURE, "Action not found/invalid\n"); + + rules[i] = (struct onvm_fw_rule *) malloc(sizeof(struct onvm_fw_rule)); + onvm_pkt_parse_ip(rules_ip->valuestring, &rules[i]->src_ip); + rules[i]->depth = depth->valueint; + rules[i]->action = action->valueint; + rules_json = rules_json->next; + i++; + } + cJSON_Delete(rules_json); + + return rules; +} + +int main(int argc, char *argv[]) { + struct onvm_nf_local_ctx *nf_local_ctx; + struct onvm_nf_function_table *nf_function_table; + struct onvm_fw_rule **rules; + int arg_offset, num_rules; + + const char *progname = argv[0]; + stats.pkt_drop = 0; + stats.pkt_accept = 0; + + nf_local_ctx = onvm_nflib_init_nf_local_ctx(); + onvm_nflib_start_signal_handler(nf_local_ctx, NULL); + + nf_function_table = onvm_nflib_init_nf_function_table(); + nf_function_table->pkt_handler = &packet_handler; + + if ((arg_offset = onvm_nflib_init(argc, argv, NF_TAG, nf_local_ctx, nf_function_table)) < 0) { + onvm_nflib_stop(nf_local_ctx); + if (arg_offset == ONVM_SIGNAL_TERMINATION) { + printf("Exiting due to user termination\n"); + return 0; + } else { + rte_exit(EXIT_FAILURE, "Failed ONVM init\n"); + } + } + + argc -= arg_offset; + argv += arg_offset; + + if (parse_app_args(argc, argv, progname) < 0) { + onvm_nflib_stop(nf_local_ctx); + rte_exit(EXIT_FAILURE, "Invalid command-line arguments\n"); + } + + rules = setup_rules(&num_rules, rule_file); + lpm_setup(rules, num_rules); + onvm_nflib_run(nf_local_ctx); + + lpm_teardown(rules, num_rules); + onvm_nflib_stop(nf_local_ctx); + printf("If we reach here, program is ending\n"); + return 0; +} diff --git a/examples/firewall/go.sh b/examples/firewall/go.sh new file mode 120000 index 000000000..40babd806 --- /dev/null +++ b/examples/firewall/go.sh @@ -0,0 +1 @@ +../go.sh \ No newline at end of file diff --git a/examples/firewall/rules.json b/examples/firewall/rules.json new file mode 100755 index 000000000..0a48150f2 --- /dev/null +++ b/examples/firewall/rules.json @@ -0,0 +1,13 @@ +{ + "rule1": { + "ip": "10.11.1.15", + "depth": 32, + "action": 0 + }, + + "rule2": { + "ip": "10.11.1.16", + "depth": 32, + "action": 0 + } +} \ No newline at end of file diff --git a/examples/flow_table/flow_table.c b/examples/flow_table/flow_table.c index 32d5d30f9..43fc26cf2 100644 --- a/examples/flow_table/flow_table.c +++ b/examples/flow_table/flow_table.c @@ -5,8 +5,8 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,43 +38,45 @@ * flow_table.c - a simple flow table NF ********************************************************************/ -#include -#include -#include +#include +#include #include #include -#include -#include +#include +#include #include -#include #include +#include +#include #include -#include -#include -#include #include #include +#include #include -#include -#include +#include #include +#include +#include +#include -#include "onvm_nflib.h" -#include "onvm_pkt_helper.h" #include "flow_table.h" -#include "sdn.h" -#include "onvm_flow_table.h" #include "onvm_flow_dir.h" +#include "onvm_flow_table.h" +#include "onvm_nflib.h" +#include "onvm_pkt_helper.h" #include "onvm_sc_common.h" +#include "sdn.h" #define NF_TAG "flow_table" /* Struct that contains information about this NF */ -struct onvm_nf_info *nf_info; +struct onvm_nf_local_ctx *global_termination_context; + +struct onvm_nf *nf; -struct rte_ring* ring_to_sdn; -struct rte_ring* ring_from_sdn; +struct rte_ring *ring_to_sdn; +struct rte_ring *ring_from_sdn; #define SDN_RING_SIZE 65536 /* number of package between each print */ @@ -90,18 +92,16 @@ setup_rings(void) { ring_to_sdn = rte_ring_lookup("ring_to_sdn"); ring_from_sdn = rte_ring_lookup("ring_from_sdn"); if (ring_to_sdn) { - rte_ring_free(ring_to_sdn); + rte_ring_free(ring_to_sdn); } if (ring_from_sdn) { - rte_ring_free(ring_from_sdn); + rte_ring_free(ring_from_sdn); } - ring_to_sdn = rte_ring_create("ring_to_sdn", SDN_RING_SIZE, - rte_socket_id(), RING_F_SP_ENQ | RING_F_SC_DEQ); - ring_from_sdn = rte_ring_create("ring_from_sdn", SDN_RING_SIZE, - rte_socket_id(), RING_F_SP_ENQ | RING_F_SC_DEQ); - if(ring_to_sdn == NULL || ring_from_sdn == NULL) { - onvm_nflib_stop(nf_info); + ring_to_sdn = rte_ring_create("ring_to_sdn", SDN_RING_SIZE, rte_socket_id(), RING_F_SP_ENQ | RING_F_SC_DEQ); + ring_from_sdn = rte_ring_create("ring_from_sdn", SDN_RING_SIZE, rte_socket_id(), RING_F_SP_ENQ | RING_F_SC_DEQ); + if (ring_to_sdn == NULL || ring_from_sdn == NULL) { + onvm_nflib_stop(global_termination_context); rte_exit(EXIT_FAILURE, "Unable to create SDN rings\n"); } } @@ -123,35 +123,34 @@ parse_app_args(int argc, char *argv[]) { opterr = 0; - while ((c = getopt (argc, argv, "p:")) != -1) + while ((c = getopt(argc, argv, "p:")) != -1) switch (c) { - case 'p': - print_delay = strtoul(optarg, NULL, 10); - break; - case '?': - usage(progname); - if (optopt == 'p') - fprintf(stderr, "Option -%c requires an argument.\n", optopt); - else if (isprint(optopt)) - fprintf(stderr, "Unknown option `-%c'.\n", optopt); - else - fprintf(stderr, "Unknown option character `\\x%x'.\n", optopt); - return -1; - default: - return -1; + case 'p': + print_delay = strtoul(optarg, NULL, 10); + break; + case '?': + usage(progname); + if (optopt == 'p') + fprintf(stderr, "Option -%c requires an argument.\n", optopt); + else if (isprint(optopt)) + fprintf(stderr, "Unknown option `-%c'.\n", optopt); + else + fprintf(stderr, "Unknown option character `\\x%x'.\n", optopt); + return -1; + default: + return -1; } return optind; } static void -do_stats_display(struct rte_mbuf* pkt, int32_t tbl_index) { - const char clr[] = { 27, '[', '2', 'J', '\0' }; - const char topLeft[] = { 27, '[', '1', ';', '1', 'H', '\0' }; +do_stats_display(struct rte_mbuf *pkt, int32_t tbl_index) { + const char clr[] = {27, '[', '2', 'J', '\0'}; + const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0'}; static uint64_t total_pkts = 0; /* Fix unused variable warnings: */ (void)pkt; - struct onvm_flow_entry *flow_entry = (struct onvm_flow_entry *)onvm_ft_get_data(sdn_ft, tbl_index); total_pkts += print_delay; @@ -160,42 +159,42 @@ do_stats_display(struct rte_mbuf* pkt, int32_t tbl_index) { printf("FLOW TABLE NF\n"); printf("-----\n"); - printf("Total pkts : %"PRIu64"\n", total_pkts); + printf("Total pkts : %" PRIu64 "\n", total_pkts); printf("Total flows : %d\n", total_flows); printf("Flow ID : %d\n", tbl_index); - printf("Flow pkts : %"PRIu64"\n", flow_entry->packet_count); - // printf("Flow Action : %d\n", flow_entry->action); - // printf("Flow Dest : %d\n", flow_entry->destination); + printf("Flow pkts : %" PRIu64 "\n", flow_entry->packet_count); + // printf("Flow Action : %d\n", flow_entry->action); + // printf("Flow Dest : %d\n", flow_entry->destination); printf("\n\n"); - #ifdef DEBUG_PRINT - struct ipv4_hdr* ip; - ip = onvm_pkt_ipv4_hdr(pkt); - if (ip != NULL) { - onvm_pkt_print(pkt); - } else { - printf("Not an IP4 packet\n"); - } - #endif +#ifdef DEBUG_PRINT + struct ipv4_hdr *ip; + ip = onvm_pkt_ipv4_hdr(pkt); + if (ip != NULL) { + onvm_pkt_print(pkt); + } else { + printf("Not an IP4 packet\n"); + } +#endif } static int -flow_table_hit(struct rte_mbuf* pkt, struct onvm_pkt_meta* meta) { +flow_table_hit(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta) { (void)pkt; - meta->chain_index = 0; - meta->action = ONVM_NF_ACTION_NEXT; - meta->destination = 0; + meta->chain_index = 0; + meta->action = ONVM_NF_ACTION_NEXT; + meta->destination = 0; return 0; } static int -flow_table_miss(struct rte_mbuf* pkt, struct onvm_pkt_meta* meta) { +flow_table_miss(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta) { int ret; /* Buffer new flows until we get response from SDN controller. */ ret = rte_ring_enqueue(ring_to_sdn, pkt); - if(ret != 0) { + if (ret != 0) { printf("ERROR enqueing to SDN ring\n"); meta->action = ONVM_NF_ACTION_DROP; meta->destination = 0; @@ -206,14 +205,15 @@ flow_table_miss(struct rte_mbuf* pkt, struct onvm_pkt_meta* meta) { } static int -packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, __attribute__((unused)) struct onvm_nf_info *nf_info) { +packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, + __attribute__((unused)) struct onvm_nf_local_ctx *nf_local_ctx) { static uint32_t counter = 0; int32_t tbl_index; int action; - struct onvm_flow_entry *flow_entry; + struct onvm_flow_entry *flow_entry; - if(!onvm_pkt_is_ipv4(pkt)) { + if (!onvm_pkt_is_ipv4(pkt)) { printf("Non-ipv4 packet\n"); meta->action = ONVM_NF_ACTION_TONF; meta->destination = def_destination; @@ -221,55 +221,71 @@ packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, __attribute__(( } tbl_index = onvm_flow_dir_get_pkt(pkt, &flow_entry); - if(tbl_index >= 0) { - #ifdef DEBUG_PRINT - printf("Found existing flow %d\n", tbl_index); - #endif + if (tbl_index >= 0) { +#ifdef DEBUG_PRINT + printf("Found existing flow %d\n", tbl_index); +#endif /* Existing flow */ action = flow_table_hit(pkt, meta); - } - else if (tbl_index == -ENOENT) { - #ifdef DEBUG_PRINT - printf("Unkown flow\n"); - #endif + } else if (tbl_index == -ENOENT) { +#ifdef DEBUG_PRINT + printf("Unkown flow\n"); +#endif /* New flow */ action = flow_table_miss(pkt, meta); - } - else { - #ifdef DEBUG_PRINT + } else { +#ifdef DEBUG_PRINT printf("Error in flow lookup: %d (ENOENT=%d, EINVAL=%d)\n", tbl_index, ENOENT, EINVAL); onvm_pkt_print(pkt); - #endif - onvm_nflib_stop(nf_info); +#endif + onvm_nflib_stop(global_termination_context); rte_exit(EXIT_FAILURE, "Error in flow lookup\n"); } if (++counter == print_delay && print_delay != 0) { - if (tbl_index >= 0) { - do_stats_display(pkt, tbl_index); - counter = 0; - } + if (tbl_index >= 0) { + do_stats_display(pkt, tbl_index); + counter = 0; + } } return action; } - -int main(int argc, char *argv[]) { - int retval; +int +main(int argc, char *argv[]) { + int arg_offset; + struct onvm_nf_local_ctx *nf_local_ctx; + struct onvm_nf_function_table *nf_function_table; unsigned sdn_core = 0; - if ((retval = onvm_nflib_init(argc, argv, NF_TAG, &nf_info)) < 0) - return -1; - argc -= retval; - argv += retval; + nf_local_ctx = onvm_nflib_init_nf_local_ctx(); + global_termination_context = nf_local_ctx; + onvm_nflib_start_signal_handler(nf_local_ctx, NULL); + + nf_function_table = onvm_nflib_init_nf_function_table(); + nf_function_table->pkt_handler = &packet_handler; + + if ((arg_offset = onvm_nflib_init(argc, argv, NF_TAG, nf_local_ctx, nf_function_table)) < 0) { + onvm_nflib_stop(nf_local_ctx); + if (arg_offset == ONVM_SIGNAL_TERMINATION) { + printf("Exiting due to user termination\n"); + return 0; + } else { + rte_exit(EXIT_FAILURE, "Failed ONVM init\n"); + } + } + + argc -= arg_offset; + argv += arg_offset; if (parse_app_args(argc, argv) < 0) { - onvm_nflib_stop(nf_info); + onvm_nflib_stop(nf_local_ctx); rte_exit(EXIT_FAILURE, "Invalid command-line arguments\n"); } printf("Flow table running on %d\n", rte_lcore_id()); - def_destination = nf_info->service_id + 1; + nf = nf_local_ctx->nf; + def_destination = nf_local_ctx->nf->service_id + 1; printf("Setting up hash table with default destination: %d\n", def_destination); total_flows = 0; @@ -280,10 +296,12 @@ int main(int argc, char *argv[]) { sdn_core = rte_get_next_lcore(sdn_core, 1, 1); rte_eal_remote_launch(setup_securechannel, NULL, sdn_core); - /* Map sdn_ft table */ - onvm_flow_dir_nf_init(); + /* Map sdn_ft table */ + onvm_flow_dir_nf_init(); printf("Starting packet handler.\n"); - onvm_nflib_run(nf_info, &packet_handler); + onvm_nflib_run(nf_local_ctx); + + onvm_nflib_stop(nf_local_ctx); printf("NF exiting...\n"); cleanup(); return 0; diff --git a/examples/flow_table/flow_table.h b/examples/flow_table/flow_table.h index 3cc4ab6c7..8475547f9 100644 --- a/examples/flow_table/flow_table.h +++ b/examples/flow_table/flow_table.h @@ -5,8 +5,8 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -55,9 +55,9 @@ usage(const char *progname) { } struct flow_table_entry { - uint32_t count; /* Number of packets in flow */ - uint8_t action; /* Action to be performed */ + uint32_t count; /* Number of packets in flow */ + uint8_t action; /* Action to be performed */ uint16_t destination; /* where to go next */ }; -#endif +#endif // _FLOW_TABLE_H_ diff --git a/examples/flow_table/msgbuf.c b/examples/flow_table/msgbuf.c index 5b02a5c00..837f1ed5a 100644 --- a/examples/flow_table/msgbuf.c +++ b/examples/flow_table/msgbuf.c @@ -5,8 +5,8 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -49,125 +49,117 @@ #include "msgbuf.h" #ifndef MIN -#define MIN(x,y) (((x) < (y))? (x) : (y)) +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) #endif -struct msgbuf * msgbuf_new(int bufsize) -{ - struct msgbuf * mbuf; - mbuf = malloc(sizeof(*mbuf)); - assert(mbuf); - mbuf->len = bufsize; - mbuf->buf = malloc(mbuf->len); - assert(mbuf->len); - mbuf->start = mbuf->end = 0; +struct msgbuf * +msgbuf_new(int bufsize) { + struct msgbuf *mbuf; + mbuf = malloc(sizeof(*mbuf)); + assert(mbuf); + mbuf->len = bufsize; + mbuf->buf = malloc(mbuf->len); + assert(mbuf->len); + mbuf->start = mbuf->end = 0; - return mbuf; + return mbuf; } -int msgbuf_read(struct msgbuf * mbuf, int sock) -{ - int count = read(sock, &mbuf->buf[mbuf->end], mbuf->len - mbuf->end); - if(count>0) - mbuf->end+=count; - if( mbuf->end >= mbuf->len) // resize buffer if need be - msgbuf_grow(mbuf); - return count; +int +msgbuf_read(struct msgbuf *mbuf, int sock) { + int count = read(sock, &mbuf->buf[mbuf->end], mbuf->len - mbuf->end); + if (count > 0) + mbuf->end += count; + if (mbuf->end >= mbuf->len) // resize buffer if need be + msgbuf_grow(mbuf); + return count; } -int msgbuf_read_all(struct msgbuf * mbuf, int sock, int len) -{ - - int count = 0; - int tmp; - while(count < len) - { - tmp =msgbuf_read(mbuf,sock); - if((tmp == 0) || - ((tmp<0) && (errno != EWOULDBLOCK ) && (errno != EINTR) && (errno != EAGAIN))) - return tmp; - if(tmp > 0) - count+=tmp; - } - return count; +int +msgbuf_read_all(struct msgbuf *mbuf, int sock, int len) { + int count = 0; + int tmp; + while (count < len) { + tmp = msgbuf_read(mbuf, sock); + if ((tmp == 0) || ((tmp < 0) && (errno != EWOULDBLOCK) && (errno != EINTR) && (errno != EAGAIN))) + return tmp; + if (tmp > 0) + count += tmp; + } + return count; } -int msgbuf_write(struct msgbuf * mbuf, int sock, int len) -{ - int send_len = mbuf->end - mbuf->start; - if (len > 0) - { - if (send_len < len) - return -1; - if (send_len > len) - send_len = len; - } - int count = write(sock, &mbuf->buf[mbuf->start], send_len); - if(count>0) - mbuf->start+=count; - if(mbuf->start >= mbuf->end) - mbuf->start = mbuf->end = 0; - return count; +int +msgbuf_write(struct msgbuf *mbuf, int sock, int len) { + int send_len = mbuf->end - mbuf->start; + if (len > 0) { + if (send_len < len) + return -1; + if (send_len > len) + send_len = len; + } + int count = write(sock, &mbuf->buf[mbuf->start], send_len); + if (count > 0) + mbuf->start += count; + if (mbuf->start >= mbuf->end) + mbuf->start = mbuf->end = 0; + return count; } -int msgbuf_write_all(struct msgbuf * mbuf, int sock, int len) -{ - int tmp=0,count=0; - while(mbuf->start < mbuf->end) - { - tmp=msgbuf_write(mbuf, sock, len); - if((tmp < 0) && - (errno != EAGAIN) && - (errno != EWOULDBLOCK) && - (errno != EINTR)) - - return tmp; - if(count > 0) - count+=tmp; - } - return count; +int +msgbuf_write_all(struct msgbuf *mbuf, int sock, int len) { + int tmp = 0, count = 0; + while (mbuf->start < mbuf->end) { + tmp = msgbuf_write(mbuf, sock, len); + if ((tmp < 0) && (errno != EAGAIN) && (errno != EWOULDBLOCK) && (errno != EINTR)) + + return tmp; + if (count > 0) + count += tmp; + } + return count; } -void msgbuf_clear(struct msgbuf * mbuf) -{ - mbuf->start = mbuf->end = 0; +void +msgbuf_clear(struct msgbuf *mbuf) { + mbuf->start = mbuf->end = 0; } -void msgbuf_grow(struct msgbuf * mbuf) -{ - mbuf->len *=2 ; - mbuf->buf = realloc(mbuf->buf, mbuf->len); - if(mbuf->buf == NULL) { - perror("msgbuf_grow failed"); - printf("buffer len: %d\n", mbuf->len); - } - assert(mbuf->buf); +void +msgbuf_grow(struct msgbuf *mbuf) { + mbuf->len *= 2; + mbuf->buf = realloc(mbuf->buf, mbuf->len); + if (mbuf->buf == NULL) { + perror("msgbuf_grow failed"); + printf("buffer len: %d\n", mbuf->len); + } + assert(mbuf->buf); } -void * msgbuf_peek(struct msgbuf *mbuf) -{ - if(mbuf->start >= mbuf->end) - return NULL; - return (void *) &mbuf->buf[mbuf->start]; +void * +msgbuf_peek(struct msgbuf *mbuf) { + if (mbuf->start >= mbuf->end) + return NULL; + return (void *)&mbuf->buf[mbuf->start]; } -int msgbuf_pull(struct msgbuf *mbuf, char * buf, int count) -{ - int min = MIN(count, mbuf->end - mbuf->start); - if( min <= 0) - return -1; - if(buf) // don't write if NULL - memcpy(buf, &mbuf->buf[mbuf->start], min); - mbuf->start+=min; - if(mbuf->start>= mbuf->end) - mbuf->start = mbuf->end = 0; - return min; +int +msgbuf_pull(struct msgbuf *mbuf, char *buf, int count) { + int min = MIN(count, mbuf->end - mbuf->start); + if (min <= 0) + return -1; + if (buf) // don't write if NULL + memcpy(buf, &mbuf->buf[mbuf->start], min); + mbuf->start += min; + if (mbuf->start >= mbuf->end) + mbuf->start = mbuf->end = 0; + return min; } -void msgbuf_push(struct msgbuf *mbuf, char *buf, int count) -{ - while((mbuf->end + count) > mbuf->len) - msgbuf_grow(mbuf); - memcpy(&mbuf->buf[mbuf->end], buf, count); - mbuf->end += count; +void +msgbuf_push(struct msgbuf *mbuf, char *buf, int count) { + while ((mbuf->end + count) > mbuf->len) + msgbuf_grow(mbuf); + memcpy(&mbuf->buf[mbuf->end], buf, count); + mbuf->end += count; } diff --git a/examples/flow_table/msgbuf.h b/examples/flow_table/msgbuf.h index 7a2a3936c..5bbb23bae 100644 --- a/examples/flow_table/msgbuf.h +++ b/examples/flow_table/msgbuf.h @@ -5,8 +5,8 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,25 +38,34 @@ * msgbuf.h - buffer, send and receive the packets to/from controller ********************************************************************/ -#ifndef MSGBUF_H -#define MSGBUF_H +#ifndef _MSGBUF_H_ +#define _MSGBUF_H_ -struct msgbuf -{ - char* buf; - int len, start, end; +struct msgbuf { + char *buf; + int len, start, end; }; -struct msgbuf* msgbuf_new(int bufsize); -int msgbuf_read(struct msgbuf *mbuf, int sock); -int msgbuf_read_all(struct msgbuf *mbuf, int sock, int len); -int msgbuf_write(struct msgbuf *mbuf, int sock, int len); -int msgbuf_write_all(struct msgbuf *mbuf, int sock, int len); -void msgbuf_grow(struct msgbuf *mbuf); -void msgbuf_clear(struct msgbuf *mbuf); -void* msgbuf_peek(struct msgbuf *mbuf); -int msgbuf_pull(struct msgbuf *mbuf, char *buf, int count); -void msgbuf_push(struct msgbuf *mbuf, char *buf, int count); +struct msgbuf * +msgbuf_new(int bufsize); +int +msgbuf_read(struct msgbuf *mbuf, int sock); +int +msgbuf_read_all(struct msgbuf *mbuf, int sock, int len); +int +msgbuf_write(struct msgbuf *mbuf, int sock, int len); +int +msgbuf_write_all(struct msgbuf *mbuf, int sock, int len); +void +msgbuf_grow(struct msgbuf *mbuf); +void +msgbuf_clear(struct msgbuf *mbuf); +void * +msgbuf_peek(struct msgbuf *mbuf); +int +msgbuf_pull(struct msgbuf *mbuf, char *buf, int count); +void +msgbuf_push(struct msgbuf *mbuf, char *buf, int count); #define msgbuf_count_buffered(mbuf) ((mbuf->end - mbuf->start)) -#endif +#endif // _MSGBUF_H_ diff --git a/examples/flow_table/openflow.h b/examples/flow_table/openflow.h index c0b5090d1..5d6549942 100644 --- a/examples/flow_table/openflow.h +++ b/examples/flow_table/openflow.h @@ -33,8 +33,8 @@ /* OpenFlow: protocol between controller and datapath. */ -#ifndef OPENFLOW_OPENFLOW_H -#define OPENFLOW_OPENFLOW_H 1 +#ifndef _OPENFLOW_H_ +#define _OPENFLOW_H_ 1 #ifdef __KERNEL__ #include @@ -43,12 +43,11 @@ #endif #ifdef SWIG -#define OFP_ASSERT(EXPR) /* SWIG can't handle OFP_ASSERT. */ +#define OFP_ASSERT(EXPR) /* SWIG can't handle OFP_ASSERT. */ #elif !defined(__cplusplus) /* Build-time assertion for use in a declaration context. */ -#define OFP_ASSERT(EXPR) \ - extern int (*build_assert(void))[ sizeof(struct { \ - unsigned int build_assert_failed : (EXPR) ? 1 : -1; })] +#define OFP_ASSERT(EXPR) \ + extern int(*build_assert(void))[sizeof(struct { unsigned int build_assert_failed : (EXPR) ? 1 : -1; })] #else /* __cplusplus */ #define OFP_ASSERT(_EXPR) typedef int build_assert_failed[(_EXPR) ? 1 : -1] #endif /* __cplusplus */ @@ -56,7 +55,7 @@ #ifndef SWIG #define OFP_PACKED __attribute__((packed)) #else -#define OFP_PACKED /* SWIG doesn't understand __attribute. */ +#define OFP_PACKED /* SWIG doesn't understand __attribute. */ #endif /* Version number: @@ -66,123 +65,123 @@ /* The most significant bit being set in the version field indicates an * experimental OpenFlow version. */ -#define OFP_VERSION 0x01 +#define OFP_VERSION 0x01 #define OFP_MAX_TABLE_NAME_LEN 32 -#define OFP_MAX_PORT_NAME_LEN 16 +#define OFP_MAX_PORT_NAME_LEN 16 -#define OFP_TCP_PORT 6633 -#define OFP_SSL_PORT 6633 +#define OFP_TCP_PORT 6633 +#define OFP_SSL_PORT 6633 -#define OFP_ETH_ALEN 6 /* Bytes in an Ethernet address. */ +#define OFP_ETH_ALEN 6 /* Bytes in an Ethernet address. */ /* Port numbering. Physical ports are numbered starting from 1. */ enum ofp_port { - /* Maximum number of physical switch ports. */ - OFPP_MAX = 0xff00, - - /* Fake output "ports". */ - OFPP_IN_PORT = 0xfff8, /* Send the packet out the input port. This - virtual port must be explicitly used - in order to send back out of the input - port. */ - OFPP_TABLE = 0xfff9, /* Perform actions in flow table. - NB: This can only be the destination - port for packet-out messages. */ - OFPP_NORMAL = 0xfffa, /* Process with normal L2/L3 switching. */ - OFPP_FLOOD = 0xfffb, /* All physical ports except input port and - those disabled by STP. */ - OFPP_ALL = 0xfffc, /* All physical ports except input port. */ - OFPP_CONTROLLER = 0xfffd, /* Send to controller. */ - OFPP_LOCAL = 0xfffe, /* Local openflow "port". */ - OFPP_NONE = 0xffff /* Not associated with a physical port. */ + /* Maximum number of physical switch ports. */ + OFPP_MAX = 0xff00, + + /* Fake output "ports". */ + OFPP_IN_PORT = 0xfff8, /* Send the packet out the input port. This + virtual port must be explicitly used + in order to send back out of the input + port. */ + OFPP_TABLE = 0xfff9, /* Perform actions in flow table. + NB: This can only be the destination + port for packet-out messages. */ + OFPP_NORMAL = 0xfffa, /* Process with normal L2/L3 switching. */ + OFPP_FLOOD = 0xfffb, /* All physical ports except input port and + those disabled by STP. */ + OFPP_ALL = 0xfffc, /* All physical ports except input port. */ + OFPP_CONTROLLER = 0xfffd, /* Send to controller. */ + OFPP_LOCAL = 0xfffe, /* Local openflow "port". */ + OFPP_NONE = 0xffff /* Not associated with a physical port. */ }; enum ofp_type { - /* Immutable messages. */ - OFPT_HELLO, /* Symmetric message */ - OFPT_ERROR, /* Symmetric message */ - OFPT_ECHO_REQUEST, /* Symmetric message */ - OFPT_ECHO_REPLY, /* Symmetric message */ - OFPT_VENDOR, /* Symmetric message */ + /* Immutable messages. */ + OFPT_HELLO, /* Symmetric message */ + OFPT_ERROR, /* Symmetric message */ + OFPT_ECHO_REQUEST, /* Symmetric message */ + OFPT_ECHO_REPLY, /* Symmetric message */ + OFPT_VENDOR, /* Symmetric message */ - /* Switch configuration messages. */ - OFPT_FEATURES_REQUEST, /* Controller/switch message */ - OFPT_FEATURES_REPLY, /* Controller/switch message */ - OFPT_GET_CONFIG_REQUEST, /* Controller/switch message */ - OFPT_GET_CONFIG_REPLY, /* Controller/switch message */ - OFPT_SET_CONFIG, /* Controller/switch message */ + /* Switch configuration messages. */ + OFPT_FEATURES_REQUEST, /* Controller/switch message */ + OFPT_FEATURES_REPLY, /* Controller/switch message */ + OFPT_GET_CONFIG_REQUEST, /* Controller/switch message */ + OFPT_GET_CONFIG_REPLY, /* Controller/switch message */ + OFPT_SET_CONFIG, /* Controller/switch message */ - /* Asynchronous messages. */ - OFPT_PACKET_IN, /* Async message */ - OFPT_FLOW_REMOVED, /* Async message */ - OFPT_PORT_STATUS, /* Async message */ + /* Asynchronous messages. */ + OFPT_PACKET_IN, /* Async message */ + OFPT_FLOW_REMOVED, /* Async message */ + OFPT_PORT_STATUS, /* Async message */ - /* Controller command messages. */ - OFPT_PACKET_OUT, /* Controller/switch message */ - OFPT_FLOW_MOD, /* Controller/switch message */ - OFPT_PORT_MOD, /* Controller/switch message */ + /* Controller command messages. */ + OFPT_PACKET_OUT, /* Controller/switch message */ + OFPT_FLOW_MOD, /* Controller/switch message */ + OFPT_PORT_MOD, /* Controller/switch message */ - /* Statistics messages. */ - OFPT_STATS_REQUEST, /* Controller/switch message */ - OFPT_STATS_REPLY, /* Controller/switch message */ + /* Statistics messages. */ + OFPT_STATS_REQUEST, /* Controller/switch message */ + OFPT_STATS_REPLY, /* Controller/switch message */ - /* Barrier messages. */ - OFPT_BARRIER_REQUEST, /* Controller/switch message */ - OFPT_BARRIER_REPLY, /* Controller/switch message */ + /* Barrier messages. */ + OFPT_BARRIER_REQUEST, /* Controller/switch message */ + OFPT_BARRIER_REPLY, /* Controller/switch message */ - /* Queue Configuration messages. */ - OFPT_QUEUE_GET_CONFIG_REQUEST, /* Controller/switch message */ - OFPT_QUEUE_GET_CONFIG_REPLY /* Controller/switch message */ + /* Queue Configuration messages. */ + OFPT_QUEUE_GET_CONFIG_REQUEST, /* Controller/switch message */ + OFPT_QUEUE_GET_CONFIG_REPLY /* Controller/switch message */ }; /* Header on all OpenFlow packets. */ struct ofp_header { - uint8_t version; /* OFP_VERSION. */ - uint8_t type; /* One of the OFPT_ constants. */ - uint16_t length; /* Length including this ofp_header. */ - uint32_t xid; /* Transaction id associated with this packet. - Replies use the same id as was in the request - to facilitate pairing. */ + uint8_t version; /* OFP_VERSION. */ + uint8_t type; /* One of the OFPT_ constants. */ + uint16_t length; /* Length including this ofp_header. */ + uint32_t xid; /* Transaction id associated with this packet. + Replies use the same id as was in the request + to facilitate pairing. */ }; OFP_ASSERT(sizeof(struct ofp_header) == 8); /* OFPT_HELLO. This message has an empty body, but implementations must * ignore any data included in the body, to allow for future extensions. */ struct ofp_hello { - struct ofp_header header; + struct ofp_header header; }; -#define OFP_DEFAULT_MISS_SEND_LEN 128 +#define OFP_DEFAULT_MISS_SEND_LEN 128 enum ofp_config_flags { - /* Handling of IP fragments. */ - OFPC_FRAG_NORMAL = 0, /* No special handling for fragments. */ - OFPC_FRAG_DROP = 1, /* Drop fragments. */ - OFPC_FRAG_REASM = 2, /* Reassemble (only if OFPC_IP_REASM set). */ - OFPC_FRAG_MASK = 3 + /* Handling of IP fragments. */ + OFPC_FRAG_NORMAL = 0, /* No special handling for fragments. */ + OFPC_FRAG_DROP = 1, /* Drop fragments. */ + OFPC_FRAG_REASM = 2, /* Reassemble (only if OFPC_IP_REASM set). */ + OFPC_FRAG_MASK = 3 }; /* Switch configuration. */ struct ofp_switch_config { - struct ofp_header header; - uint16_t flags; /* OFPC_* flags. */ - uint16_t miss_send_len; /* Max bytes of new flow that datapath should + struct ofp_header header; + uint16_t flags; /* OFPC_* flags. */ + uint16_t miss_send_len; /* Max bytes of new flow that datapath should send to the controller. */ }; OFP_ASSERT(sizeof(struct ofp_switch_config) == 12); /* Capabilities supported by the datapath. */ enum ofp_capabilities { - OFPC_FLOW_STATS = 1 << 0, /* Flow statistics. */ - OFPC_TABLE_STATS = 1 << 1, /* Table statistics. */ - OFPC_PORT_STATS = 1 << 2, /* Port statistics. */ - OFPC_STP = 1 << 3, /* 802.1d spanning tree. */ - OFPC_RESERVED = 1 << 4, /* Reserved, must be zero. */ - OFPC_IP_REASM = 1 << 5, /* Can reassemble IP fragments. */ - OFPC_QUEUE_STATS = 1 << 6, /* Queue statistics. */ - OFPC_ARP_MATCH_IP = 1 << 7 /* Match IP addresses in ARP pkts. */ + OFPC_FLOW_STATS = 1 << 0, /* Flow statistics. */ + OFPC_TABLE_STATS = 1 << 1, /* Table statistics. */ + OFPC_PORT_STATS = 1 << 2, /* Port statistics. */ + OFPC_STP = 1 << 3, /* 802.1d spanning tree. */ + OFPC_RESERVED = 1 << 4, /* Reserved, must be zero. */ + OFPC_IP_REASM = 1 << 5, /* Can reassemble IP fragments. */ + OFPC_QUEUE_STATS = 1 << 6, /* Queue statistics. */ + OFPC_ARP_MATCH_IP = 1 << 7 /* Match IP addresses in ARP pkts. */ }; /* Flags to indicate behavior of the physical port. These flags are @@ -190,163 +189,163 @@ enum ofp_capabilities { * used in the ofp_port_mod message to configure the port's behavior. */ enum ofp_port_config { - OFPPC_PORT_DOWN = 1 << 0, /* Port is administratively down. */ + OFPPC_PORT_DOWN = 1 << 0, /* Port is administratively down. */ - OFPPC_NO_STP = 1 << 1, /* Disable 802.1D spanning tree on port. */ - OFPPC_NO_RECV = 1 << 2, /* Drop all packets except 802.1D spanning - tree packets. */ - OFPPC_NO_RECV_STP = 1 << 3, /* Drop received 802.1D STP packets. */ - OFPPC_NO_FLOOD = 1 << 4, /* Do not include this port when flooding. */ - OFPPC_NO_FWD = 1 << 5, /* Drop packets forwarded to port. */ - OFPPC_NO_PACKET_IN = 1 << 6 /* Do not send packet-in msgs for port. */ + OFPPC_NO_STP = 1 << 1, /* Disable 802.1D spanning tree on port. */ + OFPPC_NO_RECV = 1 << 2, /* Drop all packets except 802.1D spanning + tree packets. */ + OFPPC_NO_RECV_STP = 1 << 3, /* Drop received 802.1D STP packets. */ + OFPPC_NO_FLOOD = 1 << 4, /* Do not include this port when flooding. */ + OFPPC_NO_FWD = 1 << 5, /* Drop packets forwarded to port. */ + OFPPC_NO_PACKET_IN = 1 << 6 /* Do not send packet-in msgs for port. */ }; /* Current state of the physical port. These are not configurable from * the controller. */ enum ofp_port_state { - OFPPS_LINK_DOWN = 1 << 0, /* No physical link present. */ + OFPPS_LINK_DOWN = 1 << 0, /* No physical link present. */ - /* The OFPPS_STP_* bits have no effect on switch operation. The - * controller must adjust OFPPC_NO_RECV, OFPPC_NO_FWD, and - * OFPPC_NO_PACKET_IN appropriately to fully implement an 802.1D spanning - * tree. */ - OFPPS_STP_LISTEN = 0 << 8, /* Not learning or relaying frames. */ - OFPPS_STP_LEARN = 1 << 8, /* Learning but not relaying frames. */ - OFPPS_STP_FORWARD = 2 << 8, /* Learning and relaying frames. */ - OFPPS_STP_BLOCK = 3 << 8, /* Not part of spanning tree. */ - OFPPS_STP_MASK = 3 << 8 /* Bit mask for OFPPS_STP_* values. */ + /* The OFPPS_STP_* bits have no effect on switch operation. The + * controller must adjust OFPPC_NO_RECV, OFPPC_NO_FWD, and + * OFPPC_NO_PACKET_IN appropriately to fully implement an 802.1D spanning + * tree. */ + OFPPS_STP_LISTEN = 0 << 8, /* Not learning or relaying frames. */ + OFPPS_STP_LEARN = 1 << 8, /* Learning but not relaying frames. */ + OFPPS_STP_FORWARD = 2 << 8, /* Learning and relaying frames. */ + OFPPS_STP_BLOCK = 3 << 8, /* Not part of spanning tree. */ + OFPPS_STP_MASK = 3 << 8 /* Bit mask for OFPPS_STP_* values. */ }; /* Features of physical ports available in a datapath. */ enum ofp_port_features { - OFPPF_10MB_HD = 1 << 0, /* 10 Mb half-duplex rate support. */ - OFPPF_10MB_FD = 1 << 1, /* 10 Mb full-duplex rate support. */ - OFPPF_100MB_HD = 1 << 2, /* 100 Mb half-duplex rate support. */ - OFPPF_100MB_FD = 1 << 3, /* 100 Mb full-duplex rate support. */ - OFPPF_1GB_HD = 1 << 4, /* 1 Gb half-duplex rate support. */ - OFPPF_1GB_FD = 1 << 5, /* 1 Gb full-duplex rate support. */ - OFPPF_10GB_FD = 1 << 6, /* 10 Gb full-duplex rate support. */ - OFPPF_COPPER = 1 << 7, /* Copper medium. */ - OFPPF_FIBER = 1 << 8, /* Fiber medium. */ - OFPPF_AUTONEG = 1 << 9, /* Auto-negotiation. */ - OFPPF_PAUSE = 1 << 10, /* Pause. */ - OFPPF_PAUSE_ASYM = 1 << 11 /* Asymmetric pause. */ + OFPPF_10MB_HD = 1 << 0, /* 10 Mb half-duplex rate support. */ + OFPPF_10MB_FD = 1 << 1, /* 10 Mb full-duplex rate support. */ + OFPPF_100MB_HD = 1 << 2, /* 100 Mb half-duplex rate support. */ + OFPPF_100MB_FD = 1 << 3, /* 100 Mb full-duplex rate support. */ + OFPPF_1GB_HD = 1 << 4, /* 1 Gb half-duplex rate support. */ + OFPPF_1GB_FD = 1 << 5, /* 1 Gb full-duplex rate support. */ + OFPPF_10GB_FD = 1 << 6, /* 10 Gb full-duplex rate support. */ + OFPPF_COPPER = 1 << 7, /* Copper medium. */ + OFPPF_FIBER = 1 << 8, /* Fiber medium. */ + OFPPF_AUTONEG = 1 << 9, /* Auto-negotiation. */ + OFPPF_PAUSE = 1 << 10, /* Pause. */ + OFPPF_PAUSE_ASYM = 1 << 11 /* Asymmetric pause. */ }; /* Description of a physical port */ struct ofp_phy_port { - uint16_t port_no; - uint8_t hw_addr[OFP_ETH_ALEN]; - char name[OFP_MAX_PORT_NAME_LEN]; /* Null-terminated */ + uint16_t port_no; + uint8_t hw_addr[OFP_ETH_ALEN]; + char name[OFP_MAX_PORT_NAME_LEN]; /* Null-terminated */ - uint32_t config; /* Bitmap of OFPPC_* flags. */ - uint32_t state; /* Bitmap of OFPPS_* flags. */ + uint32_t config; /* Bitmap of OFPPC_* flags. */ + uint32_t state; /* Bitmap of OFPPS_* flags. */ - /* Bitmaps of OFPPF_* that describe features. All bits zeroed if - * unsupported or unavailable. */ - uint32_t curr; /* Current features. */ - uint32_t advertised; /* Features being advertised by the port. */ - uint32_t supported; /* Features supported by the port. */ - uint32_t peer; /* Features advertised by peer. */ + /* Bitmaps of OFPPF_* that describe features. All bits zeroed if + * unsupported or unavailable. */ + uint32_t curr; /* Current features. */ + uint32_t advertised; /* Features being advertised by the port. */ + uint32_t supported; /* Features supported by the port. */ + uint32_t peer; /* Features advertised by peer. */ }; OFP_ASSERT(sizeof(struct ofp_phy_port) == 48); /* Switch features. */ struct ofp_switch_features { - struct ofp_header header; - uint64_t datapath_id; /* Datapath unique ID. The lower 48-bits are for - a MAC address, while the upper 16-bits are - implementer-defined. */ + struct ofp_header header; + uint64_t datapath_id; /* Datapath unique ID. The lower 48-bits are for + a MAC address, while the upper 16-bits are + implementer-defined. */ - uint32_t n_buffers; /* Max packets buffered at once. */ + uint32_t n_buffers; /* Max packets buffered at once. */ - uint8_t n_tables; /* Number of tables supported by datapath. */ - uint8_t pad[3]; /* Align to 64-bits. */ + uint8_t n_tables; /* Number of tables supported by datapath. */ + uint8_t pad[3]; /* Align to 64-bits. */ - /* Features. */ - uint32_t capabilities; /* Bitmap of support "ofp_capabilities". */ - uint32_t actions; /* Bitmap of supported "ofp_action_type"s. */ + /* Features. */ + uint32_t capabilities; /* Bitmap of support "ofp_capabilities". */ + uint32_t actions; /* Bitmap of supported "ofp_action_type"s. */ - /* Port info.*/ - struct ofp_phy_port ports[0]; /* Port definitions. The number of ports - is inferred from the length field in - the header. */ + /* Port info.*/ + struct ofp_phy_port ports[0]; /* Port definitions. The number of ports + is inferred from the length field in + the header. */ }; OFP_ASSERT(sizeof(struct ofp_switch_features) == 32); /* What changed about the physical port */ enum ofp_port_reason { - OFPPR_ADD, /* The port was added. */ - OFPPR_DELETE, /* The port was removed. */ - OFPPR_MODIFY /* Some attribute of the port has changed. */ + OFPPR_ADD, /* The port was added. */ + OFPPR_DELETE, /* The port was removed. */ + OFPPR_MODIFY /* Some attribute of the port has changed. */ }; /* A physical port has changed in the datapath */ struct ofp_port_status { - struct ofp_header header; - uint8_t reason; /* One of OFPPR_*. */ - uint8_t pad[7]; /* Align to 64-bits. */ - struct ofp_phy_port desc; + struct ofp_header header; + uint8_t reason; /* One of OFPPR_*. */ + uint8_t pad[7]; /* Align to 64-bits. */ + struct ofp_phy_port desc; }; OFP_ASSERT(sizeof(struct ofp_port_status) == 64); /* Modify behavior of the physical port */ struct ofp_port_mod { - struct ofp_header header; - uint16_t port_no; - uint8_t hw_addr[OFP_ETH_ALEN]; /* The hardware address is not - configurable. This is used to - sanity-check the request, so it must - be the same as returned in an - ofp_phy_port struct. */ - - uint32_t config; /* Bitmap of OFPPC_* flags. */ - uint32_t mask; /* Bitmap of OFPPC_* flags to be changed. */ - - uint32_t advertise; /* Bitmap of "ofp_port_features"s. Zero all + struct ofp_header header; + uint16_t port_no; + uint8_t hw_addr[OFP_ETH_ALEN]; /* The hardware address is not + configurable. This is used to + sanity-check the request, so it must + be the same as returned in an + ofp_phy_port struct. */ + + uint32_t config; /* Bitmap of OFPPC_* flags. */ + uint32_t mask; /* Bitmap of OFPPC_* flags to be changed. */ + + uint32_t advertise; /* Bitmap of "ofp_port_features"s. Zero all bits to prevent any action taking place. */ - uint8_t pad[4]; /* Pad to 64-bits. */ + uint8_t pad[4]; /* Pad to 64-bits. */ }; OFP_ASSERT(sizeof(struct ofp_port_mod) == 32); /* Why is this packet being sent to the controller? */ enum ofp_packet_in_reason { - OFPR_NO_MATCH, /* No matching flow. */ - OFPR_ACTION /* Action explicitly output to controller. */ + OFPR_NO_MATCH, /* No matching flow. */ + OFPR_ACTION /* Action explicitly output to controller. */ }; /* Packet received on port (datapath -> controller). */ struct ofp_packet_in { - struct ofp_header header; - uint32_t buffer_id; /* ID assigned by datapath. */ - uint16_t total_len; /* Full length of frame. */ - uint16_t in_port; /* Port on which frame was received. */ - uint8_t reason; /* Reason packet is being sent (one of OFPR_*) */ - uint8_t pad; - uint8_t data[0]; /* Ethernet frame, halfway through 32-bit word, - so the IP header is 32-bit aligned. The - amount of data is inferred from the length - field in the header. Because of padding, - offsetof(struct ofp_packet_in, data) == - sizeof(struct ofp_packet_in) - 2. */ + struct ofp_header header; + uint32_t buffer_id; /* ID assigned by datapath. */ + uint16_t total_len; /* Full length of frame. */ + uint16_t in_port; /* Port on which frame was received. */ + uint8_t reason; /* Reason packet is being sent (one of OFPR_*) */ + uint8_t pad; + uint8_t data[0]; /* Ethernet frame, halfway through 32-bit word, + so the IP header is 32-bit aligned. The + amount of data is inferred from the length + field in the header. Because of padding, + offsetof(struct ofp_packet_in, data) == + sizeof(struct ofp_packet_in) - 2. */ }; OFP_ASSERT(sizeof(struct ofp_packet_in) == 20); enum ofp_action_type { - OFPAT_OUTPUT, /* Output to switch port. */ - OFPAT_SET_VLAN_VID, /* Set the 802.1q VLAN id. */ - OFPAT_SET_VLAN_PCP, /* Set the 802.1q priority. */ - OFPAT_STRIP_VLAN, /* Strip the 802.1q header. */ - OFPAT_SET_DL_SRC, /* Ethernet source address. */ - OFPAT_SET_DL_DST, /* Ethernet destination address. */ - OFPAT_SET_NW_SRC, /* IP source address. */ - OFPAT_SET_NW_DST, /* IP destination address. */ - OFPAT_SET_NW_TOS, /* IP ToS (DSCP field, 6 bits). */ - OFPAT_SET_TP_SRC, /* TCP/UDP source port. */ - OFPAT_SET_TP_DST, /* TCP/UDP destination port. */ - OFPAT_ENQUEUE, /* Output to queue. */ - OFPAT_VENDOR = 0xffff + OFPAT_OUTPUT, /* Output to switch port. */ + OFPAT_SET_VLAN_VID, /* Set the 802.1q VLAN id. */ + OFPAT_SET_VLAN_PCP, /* Set the 802.1q priority. */ + OFPAT_STRIP_VLAN, /* Strip the 802.1q header. */ + OFPAT_SET_DL_SRC, /* Ethernet source address. */ + OFPAT_SET_DL_DST, /* Ethernet destination address. */ + OFPAT_SET_NW_SRC, /* IP source address. */ + OFPAT_SET_NW_DST, /* IP destination address. */ + OFPAT_SET_NW_TOS, /* IP ToS (DSCP field, 6 bits). */ + OFPAT_SET_TP_SRC, /* TCP/UDP source port. */ + OFPAT_SET_TP_DST, /* TCP/UDP destination port. */ + OFPAT_ENQUEUE, /* Output to queue. */ + OFPAT_VENDOR = 0xffff }; /* Action structure for OFPAT_OUTPUT, which sends packets out 'port'. @@ -354,77 +353,77 @@ enum ofp_action_type { * number of bytes to send. A 'max_len' of zero means no bytes of the * packet should be sent.*/ struct ofp_action_output { - uint16_t type; /* OFPAT_OUTPUT. */ - uint16_t len; /* Length is 8. */ - uint16_t port; /* Output port. */ - uint16_t max_len; /* Max length to send to controller. */ + uint16_t type; /* OFPAT_OUTPUT. */ + uint16_t len; /* Length is 8. */ + uint16_t port; /* Output port. */ + uint16_t max_len; /* Max length to send to controller. */ }; OFP_ASSERT(sizeof(struct ofp_action_output) == 8); /* The VLAN id is 12 bits, so we can use the entire 16 bits to indicate * special conditions. All ones is used to match that no VLAN id was * set. */ -#define OFP_VLAN_NONE 0xffff +#define OFP_VLAN_NONE 0xffff /* Action structure for OFPAT_SET_VLAN_VID. */ struct ofp_action_vlan_vid { - uint16_t type; /* OFPAT_SET_VLAN_VID. */ - uint16_t len; /* Length is 8. */ - uint16_t vlan_vid; /* VLAN id. */ - uint8_t pad[2]; + uint16_t type; /* OFPAT_SET_VLAN_VID. */ + uint16_t len; /* Length is 8. */ + uint16_t vlan_vid; /* VLAN id. */ + uint8_t pad[2]; }; OFP_ASSERT(sizeof(struct ofp_action_vlan_vid) == 8); /* Action structure for OFPAT_SET_VLAN_PCP. */ struct ofp_action_vlan_pcp { - uint16_t type; /* OFPAT_SET_VLAN_PCP. */ - uint16_t len; /* Length is 8. */ - uint8_t vlan_pcp; /* VLAN priority. */ - uint8_t pad[3]; + uint16_t type; /* OFPAT_SET_VLAN_PCP. */ + uint16_t len; /* Length is 8. */ + uint8_t vlan_pcp; /* VLAN priority. */ + uint8_t pad[3]; }; OFP_ASSERT(sizeof(struct ofp_action_vlan_pcp) == 8); /* Action structure for OFPAT_SET_DL_SRC/DST. */ struct ofp_action_dl_addr { - uint16_t type; /* OFPAT_SET_DL_SRC/DST. */ - uint16_t len; /* Length is 16. */ - uint8_t dl_addr[OFP_ETH_ALEN]; /* Ethernet address. */ - uint8_t pad[6]; + uint16_t type; /* OFPAT_SET_DL_SRC/DST. */ + uint16_t len; /* Length is 16. */ + uint8_t dl_addr[OFP_ETH_ALEN]; /* Ethernet address. */ + uint8_t pad[6]; }; OFP_ASSERT(sizeof(struct ofp_action_dl_addr) == 16); /* Action structure for OFPAT_SET_NW_SRC/DST. */ struct ofp_action_nw_addr { - uint16_t type; /* OFPAT_SET_TW_SRC/DST. */ - uint16_t len; /* Length is 8. */ - uint32_t nw_addr; /* IP address. */ + uint16_t type; /* OFPAT_SET_TW_SRC/DST. */ + uint16_t len; /* Length is 8. */ + uint32_t nw_addr; /* IP address. */ }; OFP_ASSERT(sizeof(struct ofp_action_nw_addr) == 8); /* Action structure for OFPAT_SET_TP_SRC/DST. */ struct ofp_action_tp_port { - uint16_t type; /* OFPAT_SET_TP_SRC/DST. */ - uint16_t len; /* Length is 8. */ - uint16_t tp_port; /* TCP/UDP port. */ - uint8_t pad[2]; + uint16_t type; /* OFPAT_SET_TP_SRC/DST. */ + uint16_t len; /* Length is 8. */ + uint16_t tp_port; /* TCP/UDP port. */ + uint8_t pad[2]; }; OFP_ASSERT(sizeof(struct ofp_action_tp_port) == 8); /* Action structure for OFPAT_SET_NW_TOS. */ struct ofp_action_nw_tos { - uint16_t type; /* OFPAT_SET_TW_SRC/DST. */ - uint16_t len; /* Length is 8. */ - uint8_t nw_tos; /* IP ToS (DSCP field, 6 bits). */ - uint8_t pad[3]; + uint16_t type; /* OFPAT_SET_TW_SRC/DST. */ + uint16_t len; /* Length is 8. */ + uint8_t nw_tos; /* IP ToS (DSCP field, 6 bits). */ + uint8_t pad[3]; }; OFP_ASSERT(sizeof(struct ofp_action_nw_tos) == 8); /* Action header for OFPAT_VENDOR. The rest of the body is vendor-defined. */ struct ofp_action_vendor_header { - uint16_t type; /* OFPAT_VENDOR. */ - uint16_t len; /* Length is a multiple of 8. */ - uint32_t vendor; /* Vendor ID, which takes the same form - as in "struct ofp_vendor_header". */ + uint16_t type; /* OFPAT_VENDOR. */ + uint16_t len; /* Length is a multiple of 8. */ + uint32_t vendor; /* Vendor ID, which takes the same form + as in "struct ofp_vendor_header". */ }; OFP_ASSERT(sizeof(struct ofp_action_vendor_header) == 8); @@ -432,67 +431,67 @@ OFP_ASSERT(sizeof(struct ofp_action_vendor_header) == 8); * header and any padding used to make the action 64-bit aligned. * NB: The length of an action *must* always be a multiple of eight. */ struct ofp_action_header { - uint16_t type; /* One of OFPAT_*. */ - uint16_t len; /* Length of action, including this - header. This is the length of action, - including any padding to make it - 64-bit aligned. */ - uint8_t pad[4]; + uint16_t type; /* One of OFPAT_*. */ + uint16_t len; /* Length of action, including this + header. This is the length of action, + including any padding to make it + 64-bit aligned. */ + uint8_t pad[4]; }; OFP_ASSERT(sizeof(struct ofp_action_header) == 8); /* Send packet (controller -> datapath). */ struct ofp_packet_out { - struct ofp_header header; - uint32_t buffer_id; /* ID assigned by datapath (-1 if none). */ - uint16_t in_port; /* Packet's input port (OFPP_NONE if none). */ - uint16_t actions_len; /* Size of action array in bytes. */ - struct ofp_action_header actions[0]; /* Actions. */ - /* uint8_t data[0]; */ /* Packet data. The length is inferred - from the length field in the header. - (Only meaningful if buffer_id == -1.) */ + struct ofp_header header; + uint32_t buffer_id; /* ID assigned by datapath (-1 if none). */ + uint16_t in_port; /* Packet's input port (OFPP_NONE if none). */ + uint16_t actions_len; /* Size of action array in bytes. */ + struct ofp_action_header actions[0]; /* Actions. */ + /* uint8_t data[0]; */ /* Packet data. The length is inferred + from the length field in the header. + (Only meaningful if buffer_id == -1.) */ }; OFP_ASSERT(sizeof(struct ofp_packet_out) == 16); enum ofp_flow_mod_command { - OFPFC_ADD, /* New flow. */ - OFPFC_MODIFY, /* Modify all matching flows. */ - OFPFC_MODIFY_STRICT, /* Modify entry strictly matching wildcards */ - OFPFC_DELETE, /* Delete all matching flows. */ - OFPFC_DELETE_STRICT /* Strictly match wildcards and priority. */ + OFPFC_ADD, /* New flow. */ + OFPFC_MODIFY, /* Modify all matching flows. */ + OFPFC_MODIFY_STRICT, /* Modify entry strictly matching wildcards */ + OFPFC_DELETE, /* Delete all matching flows. */ + OFPFC_DELETE_STRICT /* Strictly match wildcards and priority. */ }; /* Flow wildcards. */ enum ofp_flow_wildcards { - OFPFW_IN_PORT = 1 << 0, /* Switch input port. */ - OFPFW_DL_VLAN = 1 << 1, /* VLAN id. */ - OFPFW_DL_SRC = 1 << 2, /* Ethernet source address. */ - OFPFW_DL_DST = 1 << 3, /* Ethernet destination address. */ - OFPFW_DL_TYPE = 1 << 4, /* Ethernet frame type. */ - OFPFW_NW_PROTO = 1 << 5, /* IP protocol. */ - OFPFW_TP_SRC = 1 << 6, /* TCP/UDP source port. */ - OFPFW_TP_DST = 1 << 7, /* TCP/UDP destination port. */ - - /* IP source address wildcard bit count. 0 is exact match, 1 ignores the - * LSB, 2 ignores the 2 least-significant bits, ..., 32 and higher wildcard - * the entire field. This is the *opposite* of the usual convention where - * e.g. /24 indicates that 8 bits (not 24 bits) are wildcarded. */ - OFPFW_NW_SRC_SHIFT = 8, - OFPFW_NW_SRC_BITS = 6, - OFPFW_NW_SRC_MASK = ((1 << OFPFW_NW_SRC_BITS) - 1) << OFPFW_NW_SRC_SHIFT, - OFPFW_NW_SRC_ALL = 32 << OFPFW_NW_SRC_SHIFT, - - /* IP destination address wildcard bit count. Same format as source. */ - OFPFW_NW_DST_SHIFT = 14, - OFPFW_NW_DST_BITS = 6, - OFPFW_NW_DST_MASK = ((1 << OFPFW_NW_DST_BITS) - 1) << OFPFW_NW_DST_SHIFT, - OFPFW_NW_DST_ALL = 32 << OFPFW_NW_DST_SHIFT, - - OFPFW_DL_VLAN_PCP = 1 << 20, /* VLAN priority. */ - OFPFW_NW_TOS = 1 << 21, /* IP ToS (DSCP field, 6 bits). */ - - /* Wildcard all fields. */ - OFPFW_ALL = ((1 << 22) - 1) + OFPFW_IN_PORT = 1 << 0, /* Switch input port. */ + OFPFW_DL_VLAN = 1 << 1, /* VLAN id. */ + OFPFW_DL_SRC = 1 << 2, /* Ethernet source address. */ + OFPFW_DL_DST = 1 << 3, /* Ethernet destination address. */ + OFPFW_DL_TYPE = 1 << 4, /* Ethernet frame type. */ + OFPFW_NW_PROTO = 1 << 5, /* IP protocol. */ + OFPFW_TP_SRC = 1 << 6, /* TCP/UDP source port. */ + OFPFW_TP_DST = 1 << 7, /* TCP/UDP destination port. */ + + /* IP source address wildcard bit count. 0 is exact match, 1 ignores the + * LSB, 2 ignores the 2 least-significant bits, ..., 32 and higher wildcard + * the entire field. This is the *opposite* of the usual convention where + * e.g. /24 indicates that 8 bits (not 24 bits) are wildcarded. */ + OFPFW_NW_SRC_SHIFT = 8, + OFPFW_NW_SRC_BITS = 6, + OFPFW_NW_SRC_MASK = ((1 << OFPFW_NW_SRC_BITS) - 1) << OFPFW_NW_SRC_SHIFT, + OFPFW_NW_SRC_ALL = 32 << OFPFW_NW_SRC_SHIFT, + + /* IP destination address wildcard bit count. Same format as source. */ + OFPFW_NW_DST_SHIFT = 14, + OFPFW_NW_DST_BITS = 6, + OFPFW_NW_DST_MASK = ((1 << OFPFW_NW_DST_BITS) - 1) << OFPFW_NW_DST_SHIFT, + OFPFW_NW_DST_ALL = 32 << OFPFW_NW_DST_SHIFT, + + OFPFW_DL_VLAN_PCP = 1 << 20, /* VLAN priority. */ + OFPFW_NW_TOS = 1 << 21, /* IP ToS (DSCP field, 6 bits). */ + + /* Wildcard all fields. */ + OFPFW_ALL = ((1 << 22) - 1) }; /* The wildcards for ICMP type and code fields use the transport source @@ -504,36 +503,36 @@ enum ofp_flow_wildcards { * following MAC addresses are used as a frame length. Otherwise, the * two bytes are used as the Ethernet type. */ -#define OFP_DL_TYPE_ETH2_CUTOFF 0x0600 +#define OFP_DL_TYPE_ETH2_CUTOFF 0x0600 /* Value of dl_type to indicate that the frame does not include an * Ethernet type. */ -#define OFP_DL_TYPE_NOT_ETH_TYPE 0x05ff +#define OFP_DL_TYPE_NOT_ETH_TYPE 0x05ff /* The VLAN id is 12-bits, so we can use the entire 16 bits to indicate * special conditions. All ones indicates that no VLAN id was set. */ -#define OFP_VLAN_NONE 0xffff +#define OFP_VLAN_NONE 0xffff /* Fields to match against flows */ struct ofp_match { - uint32_t wildcards; /* Wildcard fields. */ - uint16_t in_port; /* Input switch port. */ - uint8_t dl_src[OFP_ETH_ALEN]; /* Ethernet source address. */ - uint8_t dl_dst[OFP_ETH_ALEN]; /* Ethernet destination address. */ - uint16_t dl_vlan; /* Input VLAN id. */ - uint8_t dl_vlan_pcp; /* Input VLAN priority. */ - uint8_t pad1[1]; /* Align to 64-bits */ - uint16_t dl_type; /* Ethernet frame type. */ - uint8_t nw_tos; /* IP ToS (actually DSCP field, 6 bits). */ - uint8_t nw_proto; /* IP protocol or lower 8 bits of - * ARP opcode. */ - uint8_t pad2[2]; /* Align to 64-bits */ - uint32_t nw_src; /* IP source address. */ - uint32_t nw_dst; /* IP destination address. */ - uint16_t tp_src; /* TCP/UDP source port. */ - uint16_t tp_dst; /* TCP/UDP destination port. */ + uint32_t wildcards; /* Wildcard fields. */ + uint16_t in_port; /* Input switch port. */ + uint8_t dl_src[OFP_ETH_ALEN]; /* Ethernet source address. */ + uint8_t dl_dst[OFP_ETH_ALEN]; /* Ethernet destination address. */ + uint16_t dl_vlan; /* Input VLAN id. */ + uint8_t dl_vlan_pcp; /* Input VLAN priority. */ + uint8_t pad1[1]; /* Align to 64-bits */ + uint16_t dl_type; /* Ethernet frame type. */ + uint8_t nw_tos; /* IP ToS (actually DSCP field, 6 bits). */ + uint8_t nw_proto; /* IP protocol or lower 8 bits of + * ARP opcode. */ + uint8_t pad2[2]; /* Align to 64-bits */ + uint32_t nw_src; /* IP source address. */ + uint32_t nw_dst; /* IP destination address. */ + uint16_t tp_src; /* TCP/UDP source port. */ + uint16_t tp_dst; /* TCP/UDP destination port. */ }; OFP_ASSERT(sizeof(struct ofp_match) == 40); @@ -550,60 +549,60 @@ OFP_ASSERT(sizeof(struct ofp_match) == 40); #define OFP_DEFAULT_PRIORITY 0x8000 enum ofp_flow_mod_flags { - OFPFF_SEND_FLOW_REM = 1 << 0, /* Send flow removed message when flow - * expires or is deleted. */ - OFPFF_CHECK_OVERLAP = 1 << 1, /* Check for overlapping entries first. */ - OFPFF_EMERG = 1 << 2 /* Remark this is for emergency. */ + OFPFF_SEND_FLOW_REM = 1 << 0, /* Send flow removed message when flow + * expires or is deleted. */ + OFPFF_CHECK_OVERLAP = 1 << 1, /* Check for overlapping entries first. */ + OFPFF_EMERG = 1 << 2 /* Remark this is for emergency. */ }; /* Flow setup and teardown (controller -> datapath). */ struct ofp_flow_mod { - struct ofp_header header; - struct ofp_match match; /* Fields to match */ - uint64_t cookie; /* Opaque controller-issued identifier. */ - - /* Flow actions. */ - uint16_t command; /* One of OFPFC_*. */ - uint16_t idle_timeout; /* Idle time before discarding (seconds). */ - uint16_t hard_timeout; /* Max time before discarding (seconds). */ - uint16_t priority; /* Priority level of flow entry. */ - uint32_t buffer_id; /* Buffered packet to apply to (or -1). - Not meaningful for OFPFC_DELETE*. */ - uint16_t out_port; /* For OFPFC_DELETE* commands, require - matching entries to include this as an - output port. A value of OFPP_NONE - indicates no restriction. */ - uint16_t flags; /* One of OFPFF_*. */ - struct ofp_action_header actions[0]; /* The action length is inferred - from the length field in the - header. */ + struct ofp_header header; + struct ofp_match match; /* Fields to match */ + uint64_t cookie; /* Opaque controller-issued identifier. */ + + /* Flow actions. */ + uint16_t command; /* One of OFPFC_*. */ + uint16_t idle_timeout; /* Idle time before discarding (seconds). */ + uint16_t hard_timeout; /* Max time before discarding (seconds). */ + uint16_t priority; /* Priority level of flow entry. */ + uint32_t buffer_id; /* Buffered packet to apply to (or -1). + Not meaningful for OFPFC_DELETE*. */ + uint16_t out_port; /* For OFPFC_DELETE* commands, require + matching entries to include this as an + output port. A value of OFPP_NONE + indicates no restriction. */ + uint16_t flags; /* One of OFPFF_*. */ + struct ofp_action_header actions[0]; /* The action length is inferred + from the length field in the + header. */ }; OFP_ASSERT(sizeof(struct ofp_flow_mod) == 72); /* Why was this flow removed? */ enum ofp_flow_removed_reason { - OFPRR_IDLE_TIMEOUT, /* Flow idle time exceeded idle_timeout. */ - OFPRR_HARD_TIMEOUT, /* Time exceeded hard_timeout. */ - OFPRR_DELETE /* Evicted by a DELETE flow mod. */ + OFPRR_IDLE_TIMEOUT, /* Flow idle time exceeded idle_timeout. */ + OFPRR_HARD_TIMEOUT, /* Time exceeded hard_timeout. */ + OFPRR_DELETE /* Evicted by a DELETE flow mod. */ }; /* Flow removed (datapath -> controller). */ struct ofp_flow_removed { - struct ofp_header header; - struct ofp_match match; /* Description of fields. */ - uint64_t cookie; /* Opaque controller-issued identifier. */ - - uint16_t priority; /* Priority level of flow entry. */ - uint8_t reason; /* One of OFPRR_*. */ - uint8_t pad[1]; /* Align to 32-bits. */ - - uint32_t duration_sec; /* Time flow was alive in seconds. */ - uint32_t duration_nsec; /* Time flow was alive in nanoseconds beyond - duration_sec. */ - uint16_t idle_timeout; /* Idle timeout from original flow mod. */ - uint8_t pad2[2]; /* Align to 64-bits. */ - uint64_t packet_count; - uint64_t byte_count; + struct ofp_header header; + struct ofp_match match; /* Description of fields. */ + uint64_t cookie; /* Opaque controller-issued identifier. */ + + uint16_t priority; /* Priority level of flow entry. */ + uint8_t reason; /* One of OFPRR_*. */ + uint8_t pad[1]; /* Align to 32-bits. */ + + uint32_t duration_sec; /* Time flow was alive in seconds. */ + uint32_t duration_nsec; /* Time flow was alive in nanoseconds beyond + duration_sec. */ + uint16_t idle_timeout; /* Idle timeout from original flow mod. */ + uint8_t pad2[2]; /* Align to 64-bits. */ + uint64_t packet_count; + uint64_t byte_count; }; OFP_ASSERT(sizeof(struct ofp_flow_removed) == 88); @@ -611,360 +610,360 @@ OFP_ASSERT(sizeof(struct ofp_flow_removed) == 88); * will not change in future versions of the protocol (although new values may * be added). */ enum ofp_error_type { - OFPET_HELLO_FAILED, /* Hello protocol failed. */ - OFPET_BAD_REQUEST, /* Request was not understood. */ - OFPET_BAD_ACTION, /* Error in action description. */ - OFPET_FLOW_MOD_FAILED, /* Problem modifying flow entry. */ - OFPET_PORT_MOD_FAILED, /* Port mod request failed. */ - OFPET_QUEUE_OP_FAILED /* Queue operation failed. */ + OFPET_HELLO_FAILED, /* Hello protocol failed. */ + OFPET_BAD_REQUEST, /* Request was not understood. */ + OFPET_BAD_ACTION, /* Error in action description. */ + OFPET_FLOW_MOD_FAILED, /* Problem modifying flow entry. */ + OFPET_PORT_MOD_FAILED, /* Port mod request failed. */ + OFPET_QUEUE_OP_FAILED /* Queue operation failed. */ }; /* ofp_error_msg 'code' values for OFPET_HELLO_FAILED. 'data' contains an * ASCII text string that may give failure details. */ enum ofp_hello_failed_code { - OFPHFC_INCOMPATIBLE, /* No compatible version. */ - OFPHFC_EPERM /* Permissions error. */ + OFPHFC_INCOMPATIBLE, /* No compatible version. */ + OFPHFC_EPERM /* Permissions error. */ }; /* ofp_error_msg 'code' values for OFPET_BAD_REQUEST. 'data' contains at least * the first 64 bytes of the failed request. */ enum ofp_bad_request_code { - OFPBRC_BAD_VERSION, /* ofp_header.version not supported. */ - OFPBRC_BAD_TYPE, /* ofp_header.type not supported. */ - OFPBRC_BAD_STAT, /* ofp_stats_request.type not supported. */ - OFPBRC_BAD_VENDOR, /* Vendor not supported (in ofp_vendor_header - * or ofp_stats_request or ofp_stats_reply). */ - OFPBRC_BAD_SUBTYPE, /* Vendor subtype not supported. */ - OFPBRC_EPERM, /* Permissions error. */ - OFPBRC_BAD_LEN, /* Wrong request length for type. */ - OFPBRC_BUFFER_EMPTY, /* Specified buffer has already been used. */ - OFPBRC_BUFFER_UNKNOWN /* Specified buffer does not exist. */ + OFPBRC_BAD_VERSION, /* ofp_header.version not supported. */ + OFPBRC_BAD_TYPE, /* ofp_header.type not supported. */ + OFPBRC_BAD_STAT, /* ofp_stats_request.type not supported. */ + OFPBRC_BAD_VENDOR, /* Vendor not supported (in ofp_vendor_header + * or ofp_stats_request or ofp_stats_reply). */ + OFPBRC_BAD_SUBTYPE, /* Vendor subtype not supported. */ + OFPBRC_EPERM, /* Permissions error. */ + OFPBRC_BAD_LEN, /* Wrong request length for type. */ + OFPBRC_BUFFER_EMPTY, /* Specified buffer has already been used. */ + OFPBRC_BUFFER_UNKNOWN /* Specified buffer does not exist. */ }; /* ofp_error_msg 'code' values for OFPET_BAD_ACTION. 'data' contains at least * the first 64 bytes of the failed request. */ enum ofp_bad_action_code { - OFPBAC_BAD_TYPE, /* Unknown action type. */ - OFPBAC_BAD_LEN, /* Length problem in actions. */ - OFPBAC_BAD_VENDOR, /* Unknown vendor id specified. */ - OFPBAC_BAD_VENDOR_TYPE, /* Unknown action type for vendor id. */ - OFPBAC_BAD_OUT_PORT, /* Problem validating output action. */ - OFPBAC_BAD_ARGUMENT, /* Bad action argument. */ - OFPBAC_EPERM, /* Permissions error. */ - OFPBAC_TOO_MANY, /* Can't handle this many actions. */ - OFPBAC_BAD_QUEUE /* Problem validating output queue. */ + OFPBAC_BAD_TYPE, /* Unknown action type. */ + OFPBAC_BAD_LEN, /* Length problem in actions. */ + OFPBAC_BAD_VENDOR, /* Unknown vendor id specified. */ + OFPBAC_BAD_VENDOR_TYPE, /* Unknown action type for vendor id. */ + OFPBAC_BAD_OUT_PORT, /* Problem validating output action. */ + OFPBAC_BAD_ARGUMENT, /* Bad action argument. */ + OFPBAC_EPERM, /* Permissions error. */ + OFPBAC_TOO_MANY, /* Can't handle this many actions. */ + OFPBAC_BAD_QUEUE /* Problem validating output queue. */ }; /* ofp_error_msg 'code' values for OFPET_FLOW_MOD_FAILED. 'data' contains * at least the first 64 bytes of the failed request. */ enum ofp_flow_mod_failed_code { - OFPFMFC_ALL_TABLES_FULL, /* Flow not added because of full tables. */ - OFPFMFC_OVERLAP, /* Attempted to add overlapping flow with - * CHECK_OVERLAP flag set. */ - OFPFMFC_EPERM, /* Permissions error. */ - OFPFMFC_BAD_EMERG_TIMEOUT, /* Flow not added because of non-zero idle/hard - * timeout. */ - OFPFMFC_BAD_COMMAND, /* Unknown command. */ - OFPFMFC_UNSUPPORTED /* Unsupported action list - cannot process in - * the order specified. */ + OFPFMFC_ALL_TABLES_FULL, /* Flow not added because of full tables. */ + OFPFMFC_OVERLAP, /* Attempted to add overlapping flow with + * CHECK_OVERLAP flag set. */ + OFPFMFC_EPERM, /* Permissions error. */ + OFPFMFC_BAD_EMERG_TIMEOUT, /* Flow not added because of non-zero idle/hard + * timeout. */ + OFPFMFC_BAD_COMMAND, /* Unknown command. */ + OFPFMFC_UNSUPPORTED /* Unsupported action list - cannot process in + * the order specified. */ }; /* ofp_error_msg 'code' values for OFPET_PORT_MOD_FAILED. 'data' contains * at least the first 64 bytes of the failed request. */ enum ofp_port_mod_failed_code { - OFPPMFC_BAD_PORT, /* Specified port does not exist. */ - OFPPMFC_BAD_HW_ADDR, /* Specified hardware address is wrong. */ + OFPPMFC_BAD_PORT, /* Specified port does not exist. */ + OFPPMFC_BAD_HW_ADDR, /* Specified hardware address is wrong. */ }; /* ofp_error msg 'code' values for OFPET_QUEUE_OP_FAILED. 'data' contains * at least the first 64 bytes of the failed request */ enum ofp_queue_op_failed_code { - OFPQOFC_BAD_PORT, /* Invalid port (or port does not exist). */ - OFPQOFC_BAD_QUEUE, /* Queue does not exist. */ - OFPQOFC_EPERM /* Permissions error. */ + OFPQOFC_BAD_PORT, /* Invalid port (or port does not exist). */ + OFPQOFC_BAD_QUEUE, /* Queue does not exist. */ + OFPQOFC_EPERM /* Permissions error. */ }; /* OFPT_ERROR: Error message (datapath -> controller). */ struct ofp_error_msg { - struct ofp_header header; + struct ofp_header header; - uint16_t type; - uint16_t code; - uint8_t data[0]; /* Variable-length data. Interpreted based - on the type and code. */ + uint16_t type; + uint16_t code; + uint8_t data[0]; /* Variable-length data. Interpreted based + on the type and code. */ }; OFP_ASSERT(sizeof(struct ofp_error_msg) == 12); enum ofp_stats_types { - /* Description of this OpenFlow switch. - * The request body is empty. - * The reply body is struct ofp_desc_stats. */ - OFPST_DESC, - - /* Individual flow statistics. - * The request body is struct ofp_flow_stats_request. - * The reply body is an array of struct ofp_flow_stats. */ - OFPST_FLOW, - - /* Aggregate flow statistics. - * The request body is struct ofp_aggregate_stats_request. - * The reply body is struct ofp_aggregate_stats_reply. */ - OFPST_AGGREGATE, - - /* Flow table statistics. - * The request body is empty. - * The reply body is an array of struct ofp_table_stats. */ - OFPST_TABLE, - - /* Physical port statistics. - * The request body is struct ofp_port_stats_request. - * The reply body is an array of struct ofp_port_stats. */ - OFPST_PORT, - - /* Queue statistics for a port - * The request body defines the port - * The reply body is an array of struct ofp_queue_stats */ - OFPST_QUEUE, - - /* Vendor extension. - * The request and reply bodies begin with a 32-bit vendor ID, which takes - * the same form as in "struct ofp_vendor_header". The request and reply - * bodies are otherwise vendor-defined. */ - OFPST_VENDOR = 0xffff + /* Description of this OpenFlow switch. + * The request body is empty. + * The reply body is struct ofp_desc_stats. */ + OFPST_DESC, + + /* Individual flow statistics. + * The request body is struct ofp_flow_stats_request. + * The reply body is an array of struct ofp_flow_stats. */ + OFPST_FLOW, + + /* Aggregate flow statistics. + * The request body is struct ofp_aggregate_stats_request. + * The reply body is struct ofp_aggregate_stats_reply. */ + OFPST_AGGREGATE, + + /* Flow table statistics. + * The request body is empty. + * The reply body is an array of struct ofp_table_stats. */ + OFPST_TABLE, + + /* Physical port statistics. + * The request body is struct ofp_port_stats_request. + * The reply body is an array of struct ofp_port_stats. */ + OFPST_PORT, + + /* Queue statistics for a port + * The request body defines the port + * The reply body is an array of struct ofp_queue_stats */ + OFPST_QUEUE, + + /* Vendor extension. + * The request and reply bodies begin with a 32-bit vendor ID, which takes + * the same form as in "struct ofp_vendor_header". The request and reply + * bodies are otherwise vendor-defined. */ + OFPST_VENDOR = 0xffff }; struct ofp_stats_request { - struct ofp_header header; - uint16_t type; /* One of the OFPST_* constants. */ - uint16_t flags; /* OFPSF_REQ_* flags (none yet defined). */ - uint8_t body[0]; /* Body of the request. */ + struct ofp_header header; + uint16_t type; /* One of the OFPST_* constants. */ + uint16_t flags; /* OFPSF_REQ_* flags (none yet defined). */ + uint8_t body[0]; /* Body of the request. */ }; OFP_ASSERT(sizeof(struct ofp_stats_request) == 12); enum ofp_stats_reply_flags { - OFPSF_REPLY_MORE = 1 << 0 /* More replies to follow. */ + OFPSF_REPLY_MORE = 1 << 0 /* More replies to follow. */ }; struct ofp_stats_reply { - struct ofp_header header; - uint16_t type; /* One of the OFPST_* constants. */ - uint16_t flags; /* OFPSF_REPLY_* flags. */ - uint8_t body[0]; /* Body of the reply. */ + struct ofp_header header; + uint16_t type; /* One of the OFPST_* constants. */ + uint16_t flags; /* OFPSF_REPLY_* flags. */ + uint8_t body[0]; /* Body of the reply. */ }; OFP_ASSERT(sizeof(struct ofp_stats_reply) == 12); -#define DESC_STR_LEN 256 +#define DESC_STR_LEN 256 #define SERIAL_NUM_LEN 32 /* Body of reply to OFPST_DESC request. Each entry is a NULL-terminated * ASCII string. */ struct ofp_desc_stats { - char mfr_desc[DESC_STR_LEN]; /* Manufacturer description. */ - char hw_desc[DESC_STR_LEN]; /* Hardware description. */ - char sw_desc[DESC_STR_LEN]; /* Software description. */ - char serial_num[SERIAL_NUM_LEN]; /* Serial number. */ - char dp_desc[DESC_STR_LEN]; /* Human readable description of datapath. */ + char mfr_desc[DESC_STR_LEN]; /* Manufacturer description. */ + char hw_desc[DESC_STR_LEN]; /* Hardware description. */ + char sw_desc[DESC_STR_LEN]; /* Software description. */ + char serial_num[SERIAL_NUM_LEN]; /* Serial number. */ + char dp_desc[DESC_STR_LEN]; /* Human readable description of datapath. */ }; OFP_ASSERT(sizeof(struct ofp_desc_stats) == 1056); /* Body for ofp_stats_request of type OFPST_FLOW. */ struct ofp_flow_stats_request { - struct ofp_match match; /* Fields to match. */ - uint8_t table_id; /* ID of table to read (from ofp_table_stats), - 0xff for all tables or 0xfe for emergency. */ - uint8_t pad; /* Align to 32 bits. */ - uint16_t out_port; /* Require matching entries to include this - as an output port. A value of OFPP_NONE - indicates no restriction. */ + struct ofp_match match; /* Fields to match. */ + uint8_t table_id; /* ID of table to read (from ofp_table_stats), + 0xff for all tables or 0xfe for emergency. */ + uint8_t pad; /* Align to 32 bits. */ + uint16_t out_port; /* Require matching entries to include this + as an output port. A value of OFPP_NONE + indicates no restriction. */ }; OFP_ASSERT(sizeof(struct ofp_flow_stats_request) == 44); /* Body of reply to OFPST_FLOW request. */ struct ofp_flow_stats { - uint16_t length; /* Length of this entry. */ - uint8_t table_id; /* ID of table flow came from. */ - uint8_t pad; - struct ofp_match match; /* Description of fields. */ - uint32_t duration_sec; /* Time flow has been alive in seconds. */ - uint32_t duration_nsec; /* Time flow has been alive in nanoseconds beyond - duration_sec. */ - uint16_t priority; /* Priority of the entry. Only meaningful - when this is not an exact-match entry. */ - uint16_t idle_timeout; /* Number of seconds idle before expiration. */ - uint16_t hard_timeout; /* Number of seconds before expiration. */ - uint8_t pad2[6]; /* Align to 64-bits. */ - uint64_t cookie; /* Opaque controller-issued identifier. */ - uint64_t packet_count; /* Number of packets in flow. */ - uint64_t byte_count; /* Number of bytes in flow. */ - struct ofp_action_header actions[0]; /* Actions. */ + uint16_t length; /* Length of this entry. */ + uint8_t table_id; /* ID of table flow came from. */ + uint8_t pad; + struct ofp_match match; /* Description of fields. */ + uint32_t duration_sec; /* Time flow has been alive in seconds. */ + uint32_t duration_nsec; /* Time flow has been alive in nanoseconds beyond + duration_sec. */ + uint16_t priority; /* Priority of the entry. Only meaningful + when this is not an exact-match entry. */ + uint16_t idle_timeout; /* Number of seconds idle before expiration. */ + uint16_t hard_timeout; /* Number of seconds before expiration. */ + uint8_t pad2[6]; /* Align to 64-bits. */ + uint64_t cookie; /* Opaque controller-issued identifier. */ + uint64_t packet_count; /* Number of packets in flow. */ + uint64_t byte_count; /* Number of bytes in flow. */ + struct ofp_action_header actions[0]; /* Actions. */ }; OFP_ASSERT(sizeof(struct ofp_flow_stats) == 88); /* Body for ofp_stats_request of type OFPST_AGGREGATE. */ struct ofp_aggregate_stats_request { - struct ofp_match match; /* Fields to match. */ - uint8_t table_id; /* ID of table to read (from ofp_table_stats) - 0xff for all tables or 0xfe for emergency. */ - uint8_t pad; /* Align to 32 bits. */ - uint16_t out_port; /* Require matching entries to include this - as an output port. A value of OFPP_NONE - indicates no restriction. */ + struct ofp_match match; /* Fields to match. */ + uint8_t table_id; /* ID of table to read (from ofp_table_stats) + 0xff for all tables or 0xfe for emergency. */ + uint8_t pad; /* Align to 32 bits. */ + uint16_t out_port; /* Require matching entries to include this + as an output port. A value of OFPP_NONE + indicates no restriction. */ }; OFP_ASSERT(sizeof(struct ofp_aggregate_stats_request) == 44); /* Body of reply to OFPST_AGGREGATE request. */ struct ofp_aggregate_stats_reply { - uint64_t packet_count; /* Number of packets in flows. */ - uint64_t byte_count; /* Number of bytes in flows. */ - uint32_t flow_count; /* Number of flows. */ - uint8_t pad[4]; /* Align to 64 bits. */ + uint64_t packet_count; /* Number of packets in flows. */ + uint64_t byte_count; /* Number of bytes in flows. */ + uint32_t flow_count; /* Number of flows. */ + uint8_t pad[4]; /* Align to 64 bits. */ }; OFP_ASSERT(sizeof(struct ofp_aggregate_stats_reply) == 24); /* Body of reply to OFPST_TABLE request. */ struct ofp_table_stats { - uint8_t table_id; /* Identifier of table. Lower numbered tables - are consulted first. */ - uint8_t pad[3]; /* Align to 32-bits. */ - char name[OFP_MAX_TABLE_NAME_LEN]; - uint32_t wildcards; /* Bitmap of OFPFW_* wildcards that are - supported by the table. */ - uint32_t max_entries; /* Max number of entries supported. */ - uint32_t active_count; /* Number of active entries. */ - uint64_t lookup_count; /* Number of packets looked up in table. */ - uint64_t matched_count; /* Number of packets that hit table. */ + uint8_t table_id; /* Identifier of table. Lower numbered tables + are consulted first. */ + uint8_t pad[3]; /* Align to 32-bits. */ + char name[OFP_MAX_TABLE_NAME_LEN]; + uint32_t wildcards; /* Bitmap of OFPFW_* wildcards that are + supported by the table. */ + uint32_t max_entries; /* Max number of entries supported. */ + uint32_t active_count; /* Number of active entries. */ + uint64_t lookup_count; /* Number of packets looked up in table. */ + uint64_t matched_count; /* Number of packets that hit table. */ }; OFP_ASSERT(sizeof(struct ofp_table_stats) == 64); /* Body for ofp_stats_request of type OFPST_PORT. */ struct ofp_port_stats_request { - uint16_t port_no; /* OFPST_PORT message must request statistics - * either for a single port (specified in - * port_no) or for all ports (if port_no == - * OFPP_NONE). */ - uint8_t pad[6]; + uint16_t port_no; /* OFPST_PORT message must request statistics + * either for a single port (specified in + * port_no) or for all ports (if port_no == + * OFPP_NONE). */ + uint8_t pad[6]; }; OFP_ASSERT(sizeof(struct ofp_port_stats_request) == 8); /* Body of reply to OFPST_PORT request. If a counter is unsupported, set * the field to all ones. */ struct ofp_port_stats { - uint16_t port_no; - uint8_t pad[6]; /* Align to 64-bits. */ - uint64_t rx_packets; /* Number of received packets. */ - uint64_t tx_packets; /* Number of transmitted packets. */ - uint64_t rx_bytes; /* Number of received bytes. */ - uint64_t tx_bytes; /* Number of transmitted bytes. */ - uint64_t rx_dropped; /* Number of packets dropped by RX. */ - uint64_t tx_dropped; /* Number of packets dropped by TX. */ - uint64_t rx_errors; /* Number of receive errors. This is a super-set - of more specific receive errors and should be - greater than or equal to the sum of all - rx_*_err values. */ - uint64_t tx_errors; /* Number of transmit errors. This is a super-set - of more specific transmit errors and should be - greater than or equal to the sum of all - tx_*_err values (none currently defined.) */ - uint64_t rx_frame_err; /* Number of frame alignment errors. */ - uint64_t rx_over_err; /* Number of packets with RX overrun. */ - uint64_t rx_crc_err; /* Number of CRC errors. */ - uint64_t collisions; /* Number of collisions. */ + uint16_t port_no; + uint8_t pad[6]; /* Align to 64-bits. */ + uint64_t rx_packets; /* Number of received packets. */ + uint64_t tx_packets; /* Number of transmitted packets. */ + uint64_t rx_bytes; /* Number of received bytes. */ + uint64_t tx_bytes; /* Number of transmitted bytes. */ + uint64_t rx_dropped; /* Number of packets dropped by RX. */ + uint64_t tx_dropped; /* Number of packets dropped by TX. */ + uint64_t rx_errors; /* Number of receive errors. This is a super-set + of more specific receive errors and should be + greater than or equal to the sum of all + rx_*_err values. */ + uint64_t tx_errors; /* Number of transmit errors. This is a super-set + of more specific transmit errors and should be + greater than or equal to the sum of all + tx_*_err values (none currently defined.) */ + uint64_t rx_frame_err; /* Number of frame alignment errors. */ + uint64_t rx_over_err; /* Number of packets with RX overrun. */ + uint64_t rx_crc_err; /* Number of CRC errors. */ + uint64_t collisions; /* Number of collisions. */ }; OFP_ASSERT(sizeof(struct ofp_port_stats) == 104); /* Vendor extension. */ struct ofp_vendor_header { - struct ofp_header header; /* Type OFPT_VENDOR. */ - uint32_t vendor; /* Vendor ID: - * - MSB 0: low-order bytes are IEEE OUI. - * - MSB != 0: defined by OpenFlow - * consortium. */ - /* Vendor-defined arbitrary additional data. */ + struct ofp_header header; /* Type OFPT_VENDOR. */ + uint32_t vendor; /* Vendor ID: + * - MSB 0: low-order bytes are IEEE OUI. + * - MSB != 0: defined by OpenFlow + * consortium. */ + /* Vendor-defined arbitrary additional data. */ }; OFP_ASSERT(sizeof(struct ofp_vendor_header) == 12); /* All ones is used to indicate all queues in a port (for stats retrieval). */ -#define OFPQ_ALL 0xffffffff +#define OFPQ_ALL 0xffffffff /* Min rate > 1000 means not configured. */ -#define OFPQ_MIN_RATE_UNCFG 0xffff +#define OFPQ_MIN_RATE_UNCFG 0xffff enum ofp_queue_properties { - OFPQT_NONE = 0, /* No property defined for queue (default). */ - OFPQT_MIN_RATE, /* Minimum datarate guaranteed. */ - /* Other types should be added here - * (i.e. max rate, precedence, etc). */ + OFPQT_NONE = 0, /* No property defined for queue (default). */ + OFPQT_MIN_RATE, /* Minimum datarate guaranteed. */ + /* Other types should be added here + * (i.e. max rate, precedence, etc). */ }; /* Common description for a queue. */ struct ofp_queue_prop_header { - uint16_t property; /* One of OFPQT_. */ - uint16_t len; /* Length of property, including this header. */ - uint8_t pad[4]; /* 64-bit alignemnt. */ + uint16_t property; /* One of OFPQT_. */ + uint16_t len; /* Length of property, including this header. */ + uint8_t pad[4]; /* 64-bit alignemnt. */ }; OFP_ASSERT(sizeof(struct ofp_queue_prop_header) == 8); /* Min-Rate queue property description. */ struct ofp_queue_prop_min_rate { - struct ofp_queue_prop_header prop_header; /* prop: OFPQT_MIN, len: 16. */ - uint16_t rate; /* In 1/10 of a percent; >1000 -> disabled. */ - uint8_t pad[6]; /* 64-bit alignment */ + struct ofp_queue_prop_header prop_header; /* prop: OFPQT_MIN, len: 16. */ + uint16_t rate; /* In 1/10 of a percent; >1000 -> disabled. */ + uint8_t pad[6]; /* 64-bit alignment */ }; OFP_ASSERT(sizeof(struct ofp_queue_prop_min_rate) == 16); /* Full description for a queue. */ struct ofp_packet_queue { - uint32_t queue_id; /* id for the specific queue. */ - uint16_t len; /* Length in bytes of this queue desc. */ - uint8_t pad[2]; /* 64-bit alignment. */ - struct ofp_queue_prop_header properties[0]; /* List of properties. */ + uint32_t queue_id; /* id for the specific queue. */ + uint16_t len; /* Length in bytes of this queue desc. */ + uint8_t pad[2]; /* 64-bit alignment. */ + struct ofp_queue_prop_header properties[0]; /* List of properties. */ }; OFP_ASSERT(sizeof(struct ofp_packet_queue) == 8); /* Query for port queue configuration. */ struct ofp_queue_get_config_request { - struct ofp_header header; - uint16_t port; /* Port to be queried. Should refer - to a valid physical port (i.e. < OFPP_MAX) */ - uint8_t pad[2]; /* 32-bit alignment. */ + struct ofp_header header; + uint16_t port; /* Port to be queried. Should refer + to a valid physical port (i.e. < OFPP_MAX) */ + uint8_t pad[2]; /* 32-bit alignment. */ }; OFP_ASSERT(sizeof(struct ofp_queue_get_config_request) == 12); /* Queue configuration for a given port. */ struct ofp_queue_get_config_reply { - struct ofp_header header; - uint16_t port; - uint8_t pad[6]; - struct ofp_packet_queue queues[0]; /* List of configured queues. */ + struct ofp_header header; + uint16_t port; + uint8_t pad[6]; + struct ofp_packet_queue queues[0]; /* List of configured queues. */ }; OFP_ASSERT(sizeof(struct ofp_queue_get_config_reply) == 16); /* OFPAT_ENQUEUE action struct: send packets to given queue on port. */ struct ofp_action_enqueue { - uint16_t type; /* OFPAT_ENQUEUE. */ - uint16_t len; /* Len is 16. */ - uint16_t port; /* Port that queue belongs. Should - refer to a valid physical port - (i.e. < OFPP_MAX) or OFPP_IN_PORT. */ - uint8_t pad[6]; /* Pad for 64-bit alignment. */ - uint32_t queue_id; /* Where to enqueue the packets. */ + uint16_t type; /* OFPAT_ENQUEUE. */ + uint16_t len; /* Len is 16. */ + uint16_t port; /* Port that queue belongs. Should + refer to a valid physical port + (i.e. < OFPP_MAX) or OFPP_IN_PORT. */ + uint8_t pad[6]; /* Pad for 64-bit alignment. */ + uint32_t queue_id; /* Where to enqueue the packets. */ }; OFP_ASSERT(sizeof(struct ofp_action_enqueue) == 16); struct ofp_queue_stats_request { - uint16_t port_no; /* All ports if OFPT_ALL. */ - uint8_t pad[2]; /* Align to 32-bits. */ - uint32_t queue_id; /* All queues if OFPQ_ALL. */ + uint16_t port_no; /* All ports if OFPT_ALL. */ + uint8_t pad[2]; /* Align to 32-bits. */ + uint32_t queue_id; /* All queues if OFPQ_ALL. */ }; OFP_ASSERT(sizeof(struct ofp_queue_stats_request) == 8); struct ofp_queue_stats { - uint16_t port_no; - uint8_t pad[2]; /* Align to 32-bits. */ - uint32_t queue_id; /* Queue i.d */ - uint64_t tx_bytes; /* Number of transmitted bytes. */ - uint64_t tx_packets; /* Number of transmitted packets. */ - uint64_t tx_errors; /* Number of packets dropped due to overrun. */ + uint16_t port_no; + uint8_t pad[2]; /* Align to 32-bits. */ + uint32_t queue_id; /* Queue i.d */ + uint64_t tx_bytes; /* Number of transmitted bytes. */ + uint64_t tx_packets; /* Number of transmitted packets. */ + uint64_t tx_errors; /* Number of packets dropped due to overrun. */ }; OFP_ASSERT(sizeof(struct ofp_queue_stats) == 32); -#endif /* openflow/openflow.h */ +#endif // _OPENFLOW_H_ diff --git a/examples/flow_table/sdn.c b/examples/flow_table/sdn.c index f5b6f4608..418027a55 100644 --- a/examples/flow_table/sdn.c +++ b/examples/flow_table/sdn.c @@ -5,8 +5,8 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,413 +38,397 @@ * sdn.c - contact the controller, send request and parse response ********************************************************************/ -#include -#include #include #include #include #include #include #include +#include +#include +#include +#include +#include +#include +#include #include #include #include -#include -#include -#include #include -#include #include -#include -#include -#include -#include +#include +#include +#include -#include "sdn.h" -#include "sdn_pkt_list.h" #include "onvm_common.h" -#include "onvm_nflib.h" +#include "onvm_flow_dir.h" #include "onvm_flow_table.h" -#include "setupconn.h" +#include "onvm_nflib.h" #include "onvm_sc_common.h" -#include "onvm_flow_dir.h" +#include "sdn.h" +#include "sdn_pkt_list.h" +#include "setupconn.h" -extern struct onvm_nf_info *nf_info; -extern struct rte_ring* ring_to_sdn; -extern struct rte_ring* ring_from_sdn; +extern struct onvm_nf *nf; +extern struct rte_ring *ring_to_sdn; +extern struct rte_ring *ring_from_sdn; extern uint16_t def_destination; struct onvm_ft *pkt_buf_ft; static struct ofp_switch_config Switch_config = { - .header = { OFP_VERSION, - OFPT_GET_CONFIG_REPLY, - sizeof(struct ofp_switch_config), - 0}, - .flags = 0, - .miss_send_len = 0, + .header = {OFP_VERSION, OFPT_GET_CONFIG_REPLY, sizeof(struct ofp_switch_config), 0}, .flags = 0, .miss_send_len = 0, }; -static inline uint64_t htonll(uint64_t n) -{ - return htonl(1) == 1 ? n : ((uint64_t) htonl(n) << 32) | htonl(n >> 32); +static inline uint64_t +htonll(uint64_t n) { + return htonl(1) == 1 ? n : ((uint64_t)htonl(n) << 32) | htonl(n >> 32); } -static inline uint64_t ntohll(uint64_t n) -{ - return htonl(1) == 1 ? n : ((uint64_t) ntohl(n) << 32) | ntohl(n >> 32); +static inline uint64_t +ntohll(uint64_t n) { + return htonl(1) == 1 ? n : ((uint64_t)ntohl(n) << 32) | ntohl(n >> 32); } -void datapath_init(struct datapath *dp, int sock, int bufsize, int debug) -{ - static int ID = 1 ; - struct ofp_header ofph; - dp->sock = sock; - dp->debug = debug; - dp->id = ID++; - dp->inbuf = msgbuf_new(bufsize); - dp->outbuf = msgbuf_new(bufsize); - dp->xid = 1; - dp->switch_status = START; - - ofph.version = OFP_VERSION; - ofph.type = OFPT_HELLO; - ofph.length = htons(sizeof(ofph)); - ofph.xid = htonl(1); - msgbuf_push(dp->outbuf,(char *) &ofph, sizeof(ofph)); - debug_msg(dp, " sent hello"); +void +datapath_init(struct datapath *dp, int sock, int bufsize, int debug) { + static int ID = 1; + struct ofp_header ofph; + dp->sock = sock; + dp->debug = debug; + dp->id = ID++; + dp->inbuf = msgbuf_new(bufsize); + dp->outbuf = msgbuf_new(bufsize); + dp->xid = 1; + dp->switch_status = START; + + ofph.version = OFP_VERSION; + ofph.type = OFPT_HELLO; + ofph.length = htons(sizeof(ofph)); + ofph.xid = htonl(1); + msgbuf_push(dp->outbuf, (char *)&ofph, sizeof(ofph)); + debug_msg(dp, " sent hello"); } -void datapath_set_pollfd(struct datapath *dp, struct pollfd *pfd) -{ - pfd->events = POLLIN|POLLOUT; - pfd->fd = dp->sock; +void +datapath_set_pollfd(struct datapath *dp, struct pollfd *pfd) { + pfd->events = POLLIN | POLLOUT; + pfd->fd = dp->sock; } -void datapath_handle_io(struct datapath *dp, const struct pollfd *pfd) -{ - if(pfd->revents & POLLIN) - datapath_handle_read(dp); - if(pfd->revents & POLLOUT) - datapath_handle_write(dp); +void +datapath_handle_io(struct datapath *dp, const struct pollfd *pfd) { + if (pfd->revents & POLLIN) + datapath_handle_read(dp); + if (pfd->revents & POLLOUT) + datapath_handle_write(dp); } -void datapath_handle_read(struct datapath *dp) -{ - printf("DEBUG: handle read \n"); - struct ofp_header *ofph; - struct ofp_header echo; - struct ofp_header barrier; - struct ofp_stats_request *stats_req; - char buf[BUFLEN]; - int count; - - count = msgbuf_read(dp->inbuf, dp->sock); // read any queued data - if (count <= 0) - { - fprintf(stderr, "controller msgbuf_read() = %d: ", count); - if(count < 0) - perror("msgbuf_read"); - else { - fprintf(stderr, " closed connection "); - } - fprintf(stderr, "... exiting\n"); - exit(1); - } - while((count= msgbuf_count_buffered(dp->inbuf)) >= (int)sizeof(struct ofp_header )) - { - ofph = msgbuf_peek(dp->inbuf); - if(count < ntohs(ofph->length)) - return; // msg not all there yet - msgbuf_pull(dp->inbuf, NULL, ntohs(ofph->length)); - switch(ofph->type) - { - case OFPT_HELLO: - debug_msg(dp, "got hello"); - // we already sent our own HELLO; don't respond - datapath_change_status(dp, READY_TO_SEND); - break; - case OFPT_FEATURES_REQUEST: - debug_msg(dp, "got feature_req"); - count = make_features_reply(dp->id, ofph->xid, buf, BUFLEN); - msgbuf_push(dp->outbuf, buf, count); - debug_msg(dp, "sent feature_rsp"); - datapath_change_status(dp, READY_TO_SEND); - break; - case OFPT_SET_CONFIG: - debug_msg(dp, "parsing set_config"); - parse_set_config(ofph); - break; - case OFPT_GET_CONFIG_REQUEST: - debug_msg(dp, "got get_config_request"); - count = make_config_reply(ofph->xid, buf, BUFLEN); - msgbuf_push(dp->outbuf, buf, count); - debug_msg(dp, "sent get_config_reply"); - break; - case OFPT_VENDOR: - debug_msg(dp, "got vendor"); - count = make_vendor_reply(ofph->xid, buf, BUFLEN); - msgbuf_push(dp->outbuf, buf, count); - debug_msg(dp, "sent vendor"); - break; - case OFPT_ECHO_REQUEST: - debug_msg(dp, "got echo, sent echo_resp"); - echo.version= OFP_VERSION; - echo.length = htons(sizeof(echo)); - echo.type = OFPT_ECHO_REPLY; - echo.xid = ofph->xid; - msgbuf_push(dp->outbuf,(char *) &echo, sizeof(echo)); - break; - case OFPT_BARRIER_REQUEST: - debug_msg(dp, "got barrier, sent barrier_resp"); - barrier.version= OFP_VERSION; - barrier.length = htons(sizeof(barrier)); - barrier.type = OFPT_BARRIER_REPLY; - barrier.xid = ofph->xid; - msgbuf_push(dp->outbuf,(char *) &barrier, sizeof(barrier)); - break; - case OFPT_STATS_REQUEST: - stats_req = (struct ofp_stats_request *) ofph; - if ( ntohs(stats_req->type) == OFPST_DESC ) { - count = make_stats_desc_reply(stats_req, buf); - msgbuf_push(dp->outbuf, buf, count); - debug_msg(dp, "sent description stats_reply"); - } else { - debug_msg(dp, "Silently ignoring non-desc stats_request msg\n"); - } - break; - case OFPT_FLOW_MOD: +void +datapath_handle_read(struct datapath *dp) { + printf("DEBUG: handle read \n"); + struct ofp_header *ofph; + struct ofp_header echo; + struct ofp_header barrier; + struct ofp_stats_request *stats_req; + char buf[BUFLEN]; + int count; + + count = msgbuf_read(dp->inbuf, dp->sock); // read any queued data + if (count <= 0) { + fprintf(stderr, "controller msgbuf_read() = %d: ", count); + if (count < 0) { + perror("msgbuf_read"); + } else { + fprintf(stderr, " closed connection "); + } + fprintf(stderr, "... exiting\n"); + exit(1); + } + while ((count = msgbuf_count_buffered(dp->inbuf)) >= (int)sizeof(struct ofp_header)) { + ofph = msgbuf_peek(dp->inbuf); + if (count < ntohs(ofph->length)) + return; // msg not all there yet + msgbuf_pull(dp->inbuf, NULL, ntohs(ofph->length)); + switch (ofph->type) { + case OFPT_HELLO: + debug_msg(dp, "got hello"); + // we already sent our own HELLO; don't respond + datapath_change_status(dp, READY_TO_SEND); + break; + case OFPT_FEATURES_REQUEST: + debug_msg(dp, "got feature_req"); + count = make_features_reply(dp->id, ofph->xid, buf, BUFLEN); + msgbuf_push(dp->outbuf, buf, count); + debug_msg(dp, "sent feature_rsp"); + datapath_change_status(dp, READY_TO_SEND); + break; + case OFPT_SET_CONFIG: + debug_msg(dp, "parsing set_config"); + parse_set_config(ofph); + break; + case OFPT_GET_CONFIG_REQUEST: + debug_msg(dp, "got get_config_request"); + count = make_config_reply(ofph->xid, buf, BUFLEN); + msgbuf_push(dp->outbuf, buf, count); + debug_msg(dp, "sent get_config_reply"); + break; + case OFPT_VENDOR: + debug_msg(dp, "got vendor"); + count = make_vendor_reply(ofph->xid, buf, BUFLEN); + msgbuf_push(dp->outbuf, buf, count); + debug_msg(dp, "sent vendor"); + break; + case OFPT_ECHO_REQUEST: + debug_msg(dp, "got echo, sent echo_resp"); + echo.version = OFP_VERSION; + echo.length = htons(sizeof(echo)); + echo.type = OFPT_ECHO_REPLY; + echo.xid = ofph->xid; + msgbuf_push(dp->outbuf, (char *)&echo, sizeof(echo)); + break; + case OFPT_BARRIER_REQUEST: + debug_msg(dp, "got barrier, sent barrier_resp"); + barrier.version = OFP_VERSION; + barrier.length = htons(sizeof(barrier)); + barrier.type = OFPT_BARRIER_REPLY; + barrier.xid = ofph->xid; + msgbuf_push(dp->outbuf, (char *)&barrier, sizeof(barrier)); + break; + case OFPT_STATS_REQUEST: + stats_req = (struct ofp_stats_request *)ofph; + if (ntohs(stats_req->type) == OFPST_DESC) { + count = make_stats_desc_reply(stats_req, buf); + msgbuf_push(dp->outbuf, buf, count); + debug_msg(dp, "sent description stats_reply"); + } else { + debug_msg(dp, "Silently ignoring non-desc stats_request msg\n"); + } + break; + case OFPT_FLOW_MOD: debug_msg(dp, "got flow_mod"); - struct ofp_flow_mod *fm; - fm = (struct ofp_flow_mod *) ofph; + struct ofp_flow_mod *fm; + fm = (struct ofp_flow_mod *)ofph; int ret; struct onvm_ft_ipv4_5tuple *fk; struct onvm_service_chain *sc; struct onvm_flow_entry *flow_entry = NULL; uint32_t buffer_id = ntohl(fm->buffer_id); - if (buffer_id == UINT32_MAX) { - break; - } - struct sdn_pkt_list* sdn_list; + if (buffer_id == UINT32_MAX) { + break; + } + struct sdn_pkt_list *sdn_list; fk = flow_key_extract(&fm->match); size_t actions_len = ntohs(fm->header.length) - sizeof(*fm); sc = flow_action_extract(&fm->actions[0], actions_len); ret = onvm_flow_dir_get_key(fk, &flow_entry); if (ret == -ENOENT) { ret = onvm_flow_dir_add_key(fk, &flow_entry); + } else if (ret >= 0) { + rte_free(flow_entry->key); + rte_free(flow_entry->sc); + } else { + rte_exit(EXIT_FAILURE, "onvm_flow_dir_get parameters are invalid"); } - else if (ret >= 0) { - rte_free(flow_entry->key); - rte_free(flow_entry->sc); - } - else { - rte_exit(EXIT_FAILURE, "onvm_flow_dir_get parameters are invalid"); - } memset(flow_entry, 0, sizeof(struct onvm_flow_entry)); flow_entry->key = fk; flow_entry->sc = sc; flow_entry->idle_timeout = OFP_FLOW_PERMANENT; flow_entry->hard_timeout = OFP_FLOW_PERMANENT; sdn_list = (struct sdn_pkt_list *)onvm_ft_get_data(pkt_buf_ft, buffer_id); - sdn_pkt_list_flush(nf_info, sdn_list); + sdn_pkt_list_flush(nf, sdn_list); break; - case OFPT_PORT_MOD: - debug_msg(dp, "got port_mod"); - break; - case OFPT_PACKET_OUT: - debug_msg(dp, "got packet_out"); - break; - case OFPT_ERROR: - debug_msg(dp, "got error"); - break; - default: - fprintf(stderr, "Ignoring OpenFlow message type %d\n", ofph->type); - } - } + case OFPT_PORT_MOD: + debug_msg(dp, "got port_mod"); + break; + case OFPT_PACKET_OUT: + debug_msg(dp, "got packet_out"); + break; + case OFPT_ERROR: + debug_msg(dp, "got error"); + break; + default: + fprintf(stderr, "Ignoring OpenFlow message type %d\n", ofph->type); + } + } } -void datapath_handle_write(struct datapath *dp) -{ - char buf[BUFLEN]; - int count = 0; - int buffer_id = 0; - - if (dp->switch_status == READY_TO_SEND) { - struct rte_mbuf* pkt; - int ret; - - ret = rte_ring_dequeue(ring_to_sdn, (void**)&pkt); - if (ret == 0) { - struct sdn_pkt_list* flow; - ret = onvm_ft_lookup_pkt(pkt_buf_ft, pkt, (char**) &flow); - if(ret == -ENOENT) { - #ifdef DEBUG_PRINT - printf("SDN: not in pkt buffer table, creating list. RSS=%d port=%d\n", pkt->hash.rss, pkt->port); - #endif - ret = onvm_ft_add_pkt(pkt_buf_ft, pkt, (char**) &flow); - if (ret == -ENOSPC) { - #ifdef DEBUG_PRINT - printf("Pkt buffer table no space\n"); - #endif - exit(1); +void +datapath_handle_write(struct datapath *dp) { + char buf[BUFLEN]; + int count = 0; + int buffer_id = 0; + + if (dp->switch_status == READY_TO_SEND) { + struct rte_mbuf *pkt; + int ret; + + ret = rte_ring_dequeue(ring_to_sdn, (void **)&pkt); + if (ret == 0) { + struct sdn_pkt_list *flow; + ret = onvm_ft_lookup_pkt(pkt_buf_ft, pkt, (char **)&flow); + if (ret == -ENOENT) { +#ifdef DEBUG_PRINT + printf("SDN: not in pkt buffer table, creating list. RSS=%d port=%d\n", pkt->hash.rss, + pkt->port); +#endif + ret = onvm_ft_add_pkt(pkt_buf_ft, pkt, (char **)&flow); + if (ret == -ENOSPC) { +#ifdef DEBUG_PRINT + printf("Pkt buffer table no space\n"); +#endif + exit(1); + } + } + buffer_id = ret; +#ifdef DEBUG_PRINT + printf("SDN: in pkt buffer table, adding to list. RSS=%d port=%d\n", pkt->hash.rss, pkt->port); +#endif + sdn_pkt_list_add(flow, pkt); + memset(buf, 0, BUFLEN); + if (sdn_pkt_list_get_flag(flow) == 0) { + count = make_packet_in(dp->xid++, buffer_id, buf, pkt); + sdn_pkt_list_set_flag(flow); + if (count > 0) { + msgbuf_push(dp->outbuf, buf, count); + } + } } - } - buffer_id = ret; - #ifdef DEBUG_PRINT - printf("SDN: in pkt buffer table, adding to list. RSS=%d port=%d\n", pkt->hash.rss, pkt->port); - #endif - sdn_pkt_list_add(flow, pkt); - memset(buf, 0, BUFLEN); - if (sdn_pkt_list_get_flag(flow) == 0) { - count = make_packet_in(dp->xid++, buffer_id, buf, pkt); - sdn_pkt_list_set_flag(flow); - if (count > 0) { - msgbuf_push(dp->outbuf, buf, count); - } - } - } - - } - - if (msgbuf_count_buffered(dp->outbuf) > 0) { - msgbuf_write(dp->outbuf, dp->sock, 0); - } + } + if (msgbuf_count_buffered(dp->outbuf) > 0) { + msgbuf_write(dp->outbuf, dp->sock, 0); + } } -void datapath_change_status(struct datapath *dp, int new_status) -{ +void +datapath_change_status(struct datapath *dp, int new_status) { dp->switch_status = new_status; debug_msg(dp, "next status %d", new_status); } -int parse_set_config(struct ofp_header *msg) -{ - struct ofp_switch_config *sc; - assert(msg->type == OFPT_SET_CONFIG); - sc = (struct ofp_switch_config *) msg; - memcpy(&Switch_config, sc, sizeof(struct ofp_switch_config)); +int +parse_set_config(struct ofp_header *msg) { + struct ofp_switch_config *sc; + assert(msg->type == OFPT_SET_CONFIG); + sc = (struct ofp_switch_config *)msg; + memcpy(&Switch_config, sc, sizeof(struct ofp_switch_config)); - return 0; + return 0; } -int make_config_reply( int xid, char *buf, int buflen) -{ - int len = sizeof(struct ofp_switch_config); - assert(buflen >= len); - Switch_config.header.type = OFPT_GET_CONFIG_REPLY; - Switch_config.header.xid = xid; - memcpy(buf, &Switch_config, len); +int +make_config_reply(int xid, char *buf, int buflen) { + int len = sizeof(struct ofp_switch_config); + assert(buflen >= len); + Switch_config.header.type = OFPT_GET_CONFIG_REPLY; + Switch_config.header.xid = xid; + memcpy(buf, &Switch_config, len); - return len; + return len; } -int make_features_reply(int id, int xid, char *buf, unsigned int buflen) -{ - struct ofp_switch_features *features; - const char fake[] = // stolen from wireshark - { - - 0x97,0x06,0x00,0xe0,0x04,0x01,0x00,0x00,0x00,0x00,0x76,0xa9, - 0xd4,0x0d,0x25,0x48,0x00,0x00,0x01,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x07,0xff,0x00,0x01,0x1a,0xc1,0x51,0xff,0xef,0x8a,0x76,0x65,0x74,0x68, - 0x31,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x02,0xce,0x2f,0xa2,0x87,0xf6,0x70,0x76,0x65,0x74,0x68, - 0x33,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x03,0xca,0x8a,0x1e,0xf3,0x77,0xef,0x76,0x65,0x74,0x68, - 0x35,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x04,0xfa,0xbc,0x77,0x8d,0x7e,0x0b,0x76,0x65,0x74,0x68, - 0x37,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00 - }; - - assert(buflen> sizeof(fake)); - memcpy(buf, fake, sizeof(fake)); - features = (struct ofp_switch_features *) buf; - features->header.version = OFP_VERSION; - features->header.xid = xid; - features->datapath_id = htonll(id); - - return sizeof(fake); +int +make_features_reply(int id, int xid, char *buf, unsigned int buflen) { + struct ofp_switch_features *features; + const char fake[] = { + 0x97, 0x06, 0x00, 0xe0, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x76, 0xa9, 0xd4, 0x0d, 0x25, 0x48, + 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, + 0x00, 0x01, 0x1a, 0xc1, 0x51, 0xff, 0xef, 0x8a, 0x76, 0x65, 0x74, 0x68, 0x31, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0xce, 0x2f, 0xa2, 0x87, 0xf6, 0x70, 0x76, 0x65, 0x74, 0x68, 0x33, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0xca, 0x8a, 0x1e, 0xf3, 0x77, 0xef, 0x76, 0x65, 0x74, 0x68, 0x35, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x04, 0xfa, 0xbc, 0x77, 0x8d, 0x7e, 0x0b, 0x76, 0x65, 0x74, 0x68, 0x37, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + assert(buflen > sizeof(fake)); + memcpy(buf, fake, sizeof(fake)); + features = (struct ofp_switch_features *)buf; + features->header.version = OFP_VERSION; + features->header.xid = xid; + features->datapath_id = htonll(id); + + return sizeof(fake); } -int make_stats_desc_reply(struct ofp_stats_request *req, char *buf) -{ - static struct ofp_desc_stats cbench_desc = { - .mfr_desc = "Cbench - controller I/O benchmark", - .hw_desc = "this is actually software...", - //.sw_desc = "version " VERSION, - .sw_desc = "version 1", - .serial_num= "none", - .dp_desc = "none" - }; - struct ofp_stats_reply *reply; - int len = sizeof(struct ofp_stats_reply) + sizeof(struct ofp_desc_stats); - - assert(BUFLEN > len); - assert(ntohs(req->type) == OFPST_DESC); - memcpy( buf, req, sizeof(*req)); - reply = (struct ofp_stats_reply *) buf; - reply->header.type = OFPT_STATS_REPLY; - reply->header.length = htons(len); - reply->flags = 0; - memcpy(reply->body, &cbench_desc, sizeof(cbench_desc)); - - return len; +int +make_stats_desc_reply(struct ofp_stats_request *req, char *buf) { + static struct ofp_desc_stats cbench_desc = {.mfr_desc = "Cbench - controller I/O benchmark", + .hw_desc = "this is actually software...", + // .sw_desc = "version " VERSION, + .sw_desc = "version 1", + .serial_num = "none", + .dp_desc = "none"}; + struct ofp_stats_reply *reply; + int len = sizeof(struct ofp_stats_reply) + sizeof(struct ofp_desc_stats); + + assert(BUFLEN > len); + assert(ntohs(req->type) == OFPST_DESC); + memcpy(buf, req, sizeof(*req)); + reply = (struct ofp_stats_reply *)buf; + reply->header.type = OFPT_STATS_REPLY; + reply->header.length = htons(len); + reply->flags = 0; + memcpy(reply->body, &cbench_desc, sizeof(cbench_desc)); + + return len; } -int make_vendor_reply(int xid, char *buf, unsigned int buflen) -{ - struct ofp_error_msg *e; +int +make_vendor_reply(int xid, char *buf, unsigned int buflen) { + struct ofp_error_msg *e; - assert(buflen> sizeof(struct ofp_error_msg)); - e = (struct ofp_error_msg *) buf; - e->header.type = OFPT_ERROR; - e->header.version = OFP_VERSION; - e->header.length = htons(sizeof(struct ofp_error_msg)); - e->header.xid = xid; - e->type = htons(OFPET_BAD_REQUEST); - e->code = htons(OFPBRC_BAD_VENDOR); + assert(buflen > sizeof(struct ofp_error_msg)); + e = (struct ofp_error_msg *)buf; + e->header.type = OFPT_ERROR; + e->header.version = OFP_VERSION; + e->header.length = htons(sizeof(struct ofp_error_msg)); + e->header.xid = xid; + e->type = htons(OFPET_BAD_REQUEST); + e->code = htons(OFPBRC_BAD_VENDOR); - return sizeof(struct ofp_error_msg); + return sizeof(struct ofp_error_msg); } -int debug_msg(struct datapath *dp, const char *msg, ...) -{ - va_list aq; - - if(dp->debug == 0 ) - return 0; - fprintf(stderr,"-------Switch %d: ", dp->id); - va_start(aq,msg); - vfprintf(stderr,msg,aq); - if(msg[strlen(msg)-1] != '\n') - fprintf(stderr, "\n"); - // fflush(stderr); // should be redundant, but often isn't :-( - - return 1; +int +debug_msg(struct datapath *dp, const char *msg, ...) { + va_list aq; + + if (dp->debug == 0) + return 0; + fprintf(stderr, "-------Switch %d: ", dp->id); + va_start(aq, msg); + vfprintf(stderr, msg, aq); + if (msg[strlen(msg) - 1] != '\n') + fprintf(stderr, "\n"); + // fflush(stderr); // should be redundant, but often isn't :-( + + return 1; } -void get_header(struct rte_mbuf *pkt, struct ofp_packet_in *pi) -{ +void +get_header(struct rte_mbuf *pkt, struct ofp_packet_in *pi) { char *header; header = (char *)(rte_pktmbuf_mtod(pkt, unsigned char *)); memcpy(pi->data, header, OFP_DEFAULT_MISS_SEND_LEN); } -int make_packet_in(int xid, uint32_t buffer_id, char *buf, struct rte_mbuf *pkt) -{ +int +make_packet_in(int xid, uint32_t buffer_id, char *buf, struct rte_mbuf *pkt) { struct ofp_packet_in *pi; - struct onvm_pkt_meta *meta; + struct onvm_pkt_meta *meta; int len; pi = (struct ofp_packet_in *)buf; pi->header.version = OFP_VERSION; pi->header.type = OFPT_PACKET_IN; pi->header.xid = htonl(xid); - meta = onvm_get_pkt_meta(pkt); + meta = onvm_get_pkt_meta(pkt); pi->in_port = htons(meta->src); pi->buffer_id = htonl(buffer_id); pi->reason = OFPR_NO_MATCH; @@ -457,11 +441,10 @@ int make_packet_in(int xid, uint32_t buffer_id, char *buf, struct rte_mbuf *pkt) return len; } -struct onvm_ft_ipv4_5tuple* flow_key_extract(struct ofp_match *match) -{ - struct onvm_ft_ipv4_5tuple* fk; - fk = rte_calloc("flow_key", - 1, sizeof(struct onvm_ft_ipv4_5tuple), 0); +struct onvm_ft_ipv4_5tuple * +flow_key_extract(struct ofp_match *match) { + struct onvm_ft_ipv4_5tuple *fk; + fk = rte_calloc("flow_key", 1, sizeof(struct onvm_ft_ipv4_5tuple), 0); if (fk == NULL) { rte_exit(EXIT_FAILURE, "Cannot allocate memory for flow key\n"); } @@ -469,107 +452,108 @@ struct onvm_ft_ipv4_5tuple* flow_key_extract(struct ofp_match *match) fk->src_addr = match->nw_src; fk->dst_addr = match->nw_dst; fk->proto = match->nw_proto; - fk->src_port = match->tp_src; - fk->dst_port = match->tp_dst; + fk->src_port = match->tp_src; + fk->dst_port = match->tp_dst; return fk; } -struct onvm_service_chain* -flow_action_extract(struct ofp_action_header *oah, size_t actions_len) -{ - uint8_t *p = (uint8_t *)oah; - struct onvm_service_chain *chain; - - chain = rte_calloc("service chain", - 1, sizeof(struct onvm_service_chain), 0); - if (chain == NULL) { - rte_exit(EXIT_FAILURE, "Cannot allocate memory for service chain\n"); - } - - if (actions_len == 0) { - onvm_sc_append_entry(chain, ONVM_NF_ACTION_DROP, 0); - } - else { - while (actions_len > 0) { - struct ofp_action_header *ah = (struct ofp_action_header *)p; - size_t len = htons(ah->len); - if (ah->type == htons(OFPAT_OUTPUT)) { //OFPAT_OUTPUT action - struct ofp_action_output *oao = (struct ofp_action_output *)p; - onvm_sc_append_entry(chain, ONVM_NF_ACTION_OUT, ntohs(oao->port)); - } - else if (ah->type == htons(OFPAT_ENQUEUE)) { - struct ofp_action_enqueue *oae = (struct ofp_action_enqueue *)p; - onvm_sc_append_entry(chain, ONVM_NF_ACTION_TONF, (uint16_t)(ntohl(oae->queue_id))); - } - p += len; - actions_len -= len; +struct onvm_service_chain * +flow_action_extract(struct ofp_action_header *oah, size_t actions_len) { + uint8_t *p = (uint8_t *)oah; + struct onvm_service_chain *chain; + + chain = rte_calloc("service chain", 1, sizeof(struct onvm_service_chain), 0); + if (chain == NULL) { + rte_exit(EXIT_FAILURE, "Cannot allocate memory for service chain\n"); + } + + if (actions_len == 0) { + onvm_sc_append_entry(chain, ONVM_NF_ACTION_DROP, 0); + } else { + while (actions_len > 0) { + struct ofp_action_header *ah = (struct ofp_action_header *)p; + size_t len = htons(ah->len); + if (ah->type == htons(OFPAT_OUTPUT)) { // OFPAT_OUTPUT action + struct ofp_action_output *oao = (struct ofp_action_output *)p; + onvm_sc_append_entry(chain, ONVM_NF_ACTION_OUT, ntohs(oao->port)); + } else if (ah->type == htons(OFPAT_ENQUEUE)) { + struct ofp_action_enqueue *oae = (struct ofp_action_enqueue *)p; + onvm_sc_append_entry(chain, ONVM_NF_ACTION_TONF, (uint16_t)(ntohl(oae->queue_id))); + } + p += len; + actions_len -= len; + } } - } - return chain; + return chain; } -int setup_securechannel(void *ptr) { - (void)ptr; // TODO: should take SDN controller IP/port as argument - struct datapath *dp; - int sock; - const char *controller_hostname = "localhost"; - int controller_port = 6633; - int debug = 1; - struct sdn_pkt_list *sdn_list; - int i; - - printf("SDN Connection running on %d\n", rte_lcore_id()); - - fprintf(stderr, "Connecting to controller at %s:%d \n" - "Debugging info is %s\n", - controller_hostname, controller_port, - debug == 1 ? "on":"off"); - - dp = malloc(sizeof(struct datapath)); - assert(dp); - sock = make_tcp_connection(controller_hostname, controller_port); //FIXIT - if (sock<0) { - fprintf(stderr, "make_nonblock_tcp_connection :: returned %d", sock); - exit(1); - } - - if(debug) fprintf(stderr,"Initializing switch...\n"); - fflush(stderr); - datapath_init(dp, sock, 65536, debug); - - if(debug) fprintf(stderr," :: done.\n"); - fflush(stderr); - - if(debug) fprintf(stderr,"Creating pkt buffer table...\n"); - pkt_buf_ft = onvm_ft_create(1024, sizeof(struct sdn_pkt_list)); - if(pkt_buf_ft == NULL) { - rte_exit(EXIT_FAILURE, "Unable to create pkt buffer table\n"); - } - for (i = 0; i < 1024; i++) { - sdn_list = (struct sdn_pkt_list *)onvm_ft_get_data(pkt_buf_ft, i); - sdn_pkt_list_init(sdn_list); - } - - if(debug) fprintf(stderr,"Running secure channel\n"); - run_securechannel(dp); - return 0; +int +setup_securechannel(void *ptr) { + (void)ptr; // TODO: should take SDN controller IP/port as argument + struct datapath *dp; + int sock; + const char *controller_hostname = "localhost"; + int controller_port = 6633; + int debug = 1; + struct sdn_pkt_list *sdn_list; + int i; + + printf("SDN Connection running on %d\n", rte_lcore_id()); + + fprintf(stderr, + "Connecting to controller at %s:%d \n" + "Debugging info is %s\n", + controller_hostname, controller_port, debug == 1 ? "on" : "off"); + + dp = malloc(sizeof(struct datapath)); + assert(dp); + sock = make_tcp_connection(controller_hostname, controller_port); // FIXIT + if (sock < 0) { + fprintf(stderr, "make_nonblock_tcp_connection :: returned %d", sock); + exit(1); + } + + if (debug) + fprintf(stderr, "Initializing switch...\n"); + fflush(stderr); + datapath_init(dp, sock, 65536, debug); + + if (debug) + fprintf(stderr, " :: done.\n"); + fflush(stderr); + + if (debug) + fprintf(stderr, "Creating pkt buffer table...\n"); + pkt_buf_ft = onvm_ft_create(1024, sizeof(struct sdn_pkt_list)); + if (pkt_buf_ft == NULL) { + rte_exit(EXIT_FAILURE, "Unable to create pkt buffer table\n"); + } + for (i = 0; i < 1024; i++) { + sdn_list = (struct sdn_pkt_list *)onvm_ft_get_data(pkt_buf_ft, i); + sdn_pkt_list_init(sdn_list); + } + + if (debug) + fprintf(stderr, "Running secure channel\n"); + run_securechannel(dp); + return 0; } /* loop secure channel thread and monitor io event */ -void* run_securechannel(void *dp) -{ - struct pollfd *pollfds; - dp = (struct datapath *)dp; - int n_switches = 1; - - pollfds = malloc(n_switches * sizeof(struct pollfd)); - assert(pollfds); - while(1) { - datapath_set_pollfd(dp, pollfds); - poll(pollfds, n_switches, 1000); - datapath_handle_io(dp, pollfds); - } - free(pollfds); +void * +run_securechannel(void *dp) { + struct pollfd *pollfds; + dp = (struct datapath *)dp; + int n_switches = 1; + + pollfds = malloc(n_switches * sizeof(struct pollfd)); + assert(pollfds); + while (1) { + datapath_set_pollfd(dp, pollfds); + poll(pollfds, n_switches, 1000); + datapath_handle_io(dp, pollfds); + } + free(pollfds); } diff --git a/examples/flow_table/sdn.h b/examples/flow_table/sdn.h index e17bcabc0..c183a222b 100644 --- a/examples/flow_table/sdn.h +++ b/examples/flow_table/sdn.h @@ -5,8 +5,8 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,52 +38,68 @@ * sdn.h - contact the controller, send request and parse response ********************************************************************/ - -#ifndef SDN_H -#define SDN_H +#ifndef _SDN_H_ +#define _SDN_H_ #include #include #include "msgbuf.h" -#include "openflow.h" #include "onvm_flow_table.h" +#include "openflow.h" #define NUM_BUFFER_IDS 100000 #define ETH_ADDR_LEN 6 enum handshake_status { - START = 0, - READY_TO_SEND = 99, + START = 0, + READY_TO_SEND = 99, }; - -struct datapath -{ - int id; //switch dpid - int sock; //secure channel, connected to controller - struct msgbuf *inbuf, *outbuf; //input, output buffer - int count; //number of responses received??? - int xid; //transaction id - int switch_status; //current switch status - int debug; //print debug info +struct datapath { + int id; // switch dpid + int sock; // secure channel, connected to controller + struct msgbuf *inbuf, *outbuf; // input, output buffer + int count; // number of responses received??? + int xid; // transaction id + int switch_status; // current switch status + int debug; // print debug info }; -void datapath_init(struct datapath *dp, int sock, int bufsize, int debug); -void datapath_set_pollfd(struct datapath *dp, struct pollfd *pfd); -void datapath_handle_io(struct datapath *dp, const struct pollfd *pfd); -void datapath_handle_read(struct datapath *dp); -void datapath_handle_write(struct datapath *dp); -void datapath_change_status(struct datapath *dp, int new_status); -int debug_msg(struct datapath *dp, const char *msg, ...); -int make_features_reply(int switch_id, int xid, char *buf, unsigned int buflen); -int parse_set_config(struct ofp_header *msg); -int make_packet_in(int xid, uint32_t buffer_id, char *buf, struct rte_mbuf *pkt); -int make_config_reply( int xid, char *buf, int buflen); -int make_vendor_reply(int xid, char *buf, unsigned int buflen); -int make_stats_desc_reply(struct ofp_stats_request *req, char *buf); -struct onvm_ft_ipv4_5tuple* flow_key_extract(struct ofp_match *match); -struct onvm_service_chain* flow_action_extract(struct ofp_action_header *oah, size_t actions_len); -void get_header(struct rte_mbuf *pkt, struct ofp_packet_in *pi); -int setup_securechannel(void *); -void* run_securechannel(void *dp); -#endif +void +datapath_init(struct datapath *dp, int sock, int bufsize, int debug); +void +datapath_set_pollfd(struct datapath *dp, struct pollfd *pfd); +void +datapath_handle_io(struct datapath *dp, const struct pollfd *pfd); +void +datapath_handle_read(struct datapath *dp); +void +datapath_handle_write(struct datapath *dp); +void +datapath_change_status(struct datapath *dp, int new_status); +int +debug_msg(struct datapath *dp, const char *msg, ...); +int +make_features_reply(int switch_id, int xid, char *buf, unsigned int buflen); +int +parse_set_config(struct ofp_header *msg); +int +make_packet_in(int xid, uint32_t buffer_id, char *buf, struct rte_mbuf *pkt); +int +make_config_reply(int xid, char *buf, int buflen); +int +make_vendor_reply(int xid, char *buf, unsigned int buflen); +int +make_stats_desc_reply(struct ofp_stats_request *req, char *buf); +struct onvm_ft_ipv4_5tuple * +flow_key_extract(struct ofp_match *match); +struct onvm_service_chain * +flow_action_extract(struct ofp_action_header *oah, size_t actions_len); +void +get_header(struct rte_mbuf *pkt, struct ofp_packet_in *pi); +int +setup_securechannel(void *); +void * +run_securechannel(void *dp); + +#endif // _SDN_H_ diff --git a/examples/flow_table/sdn_pkt_list.h b/examples/flow_table/sdn_pkt_list.h index 75e06e9b2..dd9cb0545 100644 --- a/examples/flow_table/sdn_pkt_list.h +++ b/examples/flow_table/sdn_pkt_list.h @@ -5,8 +5,8 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -45,15 +45,15 @@ #include "onvm_nflib.h" struct sdn_pkt_list { - struct sdn_pkt_entry *head; - struct sdn_pkt_entry *tail; + struct sdn_pkt_entry* head; + struct sdn_pkt_entry* tail; int flag; - int counter; + int counter; }; struct sdn_pkt_entry { - struct rte_mbuf *pkt; - struct sdn_pkt_entry *next; + struct rte_mbuf* pkt; + struct sdn_pkt_entry* next; }; // FIXME: These functions should have return codes to indicate errors. @@ -64,60 +64,60 @@ sdn_pkt_list_init(struct sdn_pkt_list* list) { list->head = NULL; list->tail = NULL; list->flag = 0; - list->counter = 0; + list->counter = 0; } static inline void -sdn_pkt_list_add(struct sdn_pkt_list* list, struct rte_mbuf *pkt) { - list->counter++; +sdn_pkt_list_add(struct sdn_pkt_list* list, struct rte_mbuf* pkt) { + list->counter++; /* FIXME: check for malloc errors */ struct sdn_pkt_entry* entry; - entry = (struct sdn_pkt_entry*) calloc(1, sizeof(struct sdn_pkt_entry)); + entry = (struct sdn_pkt_entry*)calloc(1, sizeof(struct sdn_pkt_entry)); entry->pkt = pkt; entry->next = NULL; - if (list->head == NULL) { - list->head = entry; - list->tail = list->head; - } - else { - list->tail->next = entry; - list->tail = entry; - } + if (list->head == NULL) { + list->head = entry; + list->tail = list->head; + } else { + list->tail->next = entry; + list->tail = entry; + } } static inline void sdn_pkt_list_set_flag(struct sdn_pkt_list* list) { - list->flag = 1; + list->flag = 1; } static inline int sdn_pkt_list_get_flag(struct sdn_pkt_list* list) { - return list->flag; + return list->flag; } static inline void -sdn_pkt_list_flush(struct onvm_nf_info *nf_info, struct sdn_pkt_list* list) { - struct sdn_pkt_entry* entry; - struct rte_mbuf *pkt; - struct onvm_pkt_meta* meta; +sdn_pkt_list_flush(struct onvm_nf* nf, struct sdn_pkt_list* list) { + struct sdn_pkt_entry* entry; + struct rte_mbuf* pkt; + struct onvm_pkt_meta* meta; - printf("list items:%d\n", list->counter); + printf("list items:%d\n", list->counter); - while(list->head) { - entry = list->head; - list->head = entry->next; - pkt = entry->pkt; - meta = onvm_get_pkt_meta(pkt); - meta->action = ONVM_NF_ACTION_NEXT; - meta->chain_index = 0; - onvm_nflib_return_pkt(nf_info, pkt); - free(entry); - list->counter--; - } + while (list->head) { + entry = list->head; + list->head = entry->next; + pkt = entry->pkt; + meta = onvm_get_pkt_meta(pkt); + meta->action = ONVM_NF_ACTION_NEXT; + meta->chain_index = 0; + onvm_nflib_return_pkt(nf, pkt); + free(entry); + list->counter--; + } - list->flag = 0; - list->head = NULL; - list->tail = NULL; - list->counter = 0; + list->flag = 0; + list->head = NULL; + list->tail = NULL; + list->counter = 0; } -#endif + +#endif // _SDN_PKT_LIST_H_ diff --git a/examples/flow_table/setupconn.c b/examples/flow_table/setupconn.c index 0ac39c5a4..2d8b5e57d 100644 --- a/examples/flow_table/setupconn.c +++ b/examples/flow_table/setupconn.c @@ -5,8 +5,8 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -44,120 +44,114 @@ #include #include #include +#include #include #include #include -#include -#include -#include #include -#include #include +#include +#include +#include #include "setupconn.h" /**********************************************************************/ -int timeout_connect(int fd, const char *hostname, int port, int mstimeout) { - int ret = 0; - int flags; - fd_set fds; - struct timeval tv; - struct addrinfo *res=NULL; - struct addrinfo hints; - char sport[BUFLEN]; - int err; +int +timeout_connect(int fd, const char *hostname, int port, int mstimeout) { + int ret = 0; + int flags; + fd_set fds; + struct timeval tv; + struct addrinfo *res = NULL; + struct addrinfo hints; + char sport[BUFLEN]; + int err; - hints.ai_flags = 0; - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - hints.ai_addrlen = 0; - hints.ai_addr = NULL; - hints.ai_canonname = NULL; - hints.ai_next = NULL; + hints.ai_flags = 0; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + hints.ai_addrlen = 0; + hints.ai_addr = NULL; + hints.ai_canonname = NULL; + hints.ai_next = NULL; - snprintf(sport,BUFLEN,"%d",port); + snprintf(sport, BUFLEN, "%d", port); - err = getaddrinfo(hostname,sport,&hints,&res); - if(err|| (res==NULL)) - { - if(res) - freeaddrinfo(res); - return -1; - } - // set non blocking - if((flags = fcntl(fd, F_GETFL)) < 0) { - fprintf(stderr, "timeout_connect: unable to get socket flags\n"); - freeaddrinfo(res); - return -1; - } - if(fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) { - fprintf(stderr, "timeout_connect: unable to put the socket in non-blocking mode\n"); - freeaddrinfo(res); - return -1; - } - FD_ZERO(&fds); - FD_SET(fd, &fds); + err = getaddrinfo(hostname, sport, &hints, &res); + if (err || (res == NULL)) { + if (res) + freeaddrinfo(res); + return -1; + } + // set non blocking + if ((flags = fcntl(fd, F_GETFL)) < 0) { + fprintf(stderr, "timeout_connect: unable to get socket flags\n"); + freeaddrinfo(res); + return -1; + } + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) { + fprintf(stderr, "timeout_connect: unable to put the socket in non-blocking mode\n"); + freeaddrinfo(res); + return -1; + } + FD_ZERO(&fds); + FD_SET(fd, &fds); - if(mstimeout >= 0) - { - tv.tv_sec = mstimeout / 1000; - tv.tv_usec = (mstimeout % 1000) * 1000; + if (mstimeout >= 0) { + tv.tv_sec = mstimeout / 1000; + tv.tv_usec = (mstimeout % 1000) * 1000; - errno = 0; + errno = 0; - if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) - { - if((errno != EWOULDBLOCK) && (errno != EINPROGRESS)) - { - fprintf(stderr, "timeout_connect: error connecting: %d\n", errno); - freeaddrinfo(res); - return -1; - } - } - ret = select(fd+1, NULL, &fds, NULL, &tv); - } - freeaddrinfo(res); + if (connect(fd, res->ai_addr, res->ai_addrlen) < 0) { + if ((errno != EWOULDBLOCK) && (errno != EINPROGRESS)) { + fprintf(stderr, "timeout_connect: error connecting: %d\n", errno); + freeaddrinfo(res); + return -1; + } + } + ret = select(fd + 1, NULL, &fds, NULL, &tv); + } + freeaddrinfo(res); - if(ret != 1) - { - if(ret == 0) - return -1; - else - return ret; - } - return 0; + if (ret != 1) { + if (ret == 0) + return -1; + else + return ret; + } + return 0; } /**********************************************************************/ -int make_tcp_connection(const char *hostname, unsigned short port) -{ - struct sockaddr_in local; - int s; - int err; - int mstimeout = 3000; +int +make_tcp_connection(const char *hostname, unsigned short port) { + struct sockaddr_in local; + int s; + int err; + int mstimeout = 3000; - s = socket(AF_INET, SOCK_STREAM, 0); - if(s<0) { - perror("make_tcp_connection: socket"); - exit(1); - } - local.sin_family = PF_INET; - local.sin_addr.s_addr = INADDR_ANY; - local.sin_port = htons(INADDR_ANY); + s = socket(AF_INET, SOCK_STREAM, 0); + if (s < 0) { + perror("make_tcp_connection: socket"); + exit(1); + } + local.sin_family = PF_INET; + local.sin_addr.s_addr = INADDR_ANY; + local.sin_port = htons(INADDR_ANY); - err=bind(s,(struct sockaddr *)&local, sizeof(local)); - if(err) - { - perror("make_tcp_connection_from_port::bind"); - return -4; - } + err = bind(s, (struct sockaddr *)&local, sizeof(local)); + if (err) { + perror("make_tcp_connection_from_port::bind"); + return -4; + } - err = timeout_connect(s,hostname,port, mstimeout); - if(err) - { - perror("make_tcp_connection: connect"); - close(s); - return err; // bad connect - } - return s; + err = timeout_connect(s, hostname, port, mstimeout); + if (err) { + perror("make_tcp_connection: connect"); + close(s); + return err; // bad connect + } + return s; } diff --git a/examples/flow_table/setupconn.h b/examples/flow_table/setupconn.h index 56248fa92..9e6679f1e 100644 --- a/examples/flow_table/setupconn.h +++ b/examples/flow_table/setupconn.h @@ -5,8 +5,8 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,16 +37,17 @@ * * setupconn.h - set up the connection to sdn controller ********************************************************************/ - -#ifndef SETUPCONN_H -#define SETUPCONN_H + +#ifndef _SETUPCONN_H_ +#define _SETUPCONN_H_ #ifndef BUFLEN #define BUFLEN 65536 #endif +int +timeout_connect(int fd, const char *hostname, int port, int mstimeout); +int +make_tcp_connection(const char *hostname, unsigned short port); -int timeout_connect(int fd, const char *hostname, int port, int mstimeout); -int make_tcp_connection(const char *hostname, unsigned short port); - -#endif +#endif // _SETUPCONN_H_ diff --git a/examples/flow_tracker/flow_tracker.c b/examples/flow_tracker/flow_tracker.c index 12346728a..9437bc990 100644 --- a/examples/flow_tracker/flow_tracker.c +++ b/examples/flow_tracker/flow_tracker.c @@ -5,8 +5,8 @@ * BSD LICENSE * * Copyright(c) - * 2015-2016 George Washington University - * 2015-2016 University of California Riverside + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,26 +38,26 @@ * flow_tracker.c - an example using onvm. Stores incoming flows and prints info about them. ********************************************************************/ -#include -#include -#include +#include +#include #include #include -#include +#include +#include #include -#include #include +#include #include -#include -#include +#include #include +#include #include -#include +#include +#include "onvm_flow_table.h" #include "onvm_nflib.h" #include "onvm_pkt_helper.h" -#include "onvm_flow_table.h" #define NF_TAG "flow_tracker" #define TBL_SIZE 100 @@ -80,8 +80,6 @@ struct flow_stats { int is_active; }; -/*Struct that holds nf meta info */ -struct onvm_nf_info *nf_info; struct state_info *state_info; /* @@ -102,39 +100,39 @@ usage(const char *progname) { */ static int parse_app_args(int argc, char *argv[], const char *progname) { - int c, dst_flag = 0; - - while ((c = getopt(argc, argv, "d:p:")) != -1) { - switch(c) { - case 'd': - state_info->destination = strtoul(optarg, NULL, 10); - dst_flag = 1; - RTE_LOG(INFO, APP, "Sending packets to service ID %d\n", state_info->destination); - break; - case 'p': - state_info->print_delay = strtoul(optarg, NULL, 10); - break; - case '?': - usage(progname); - if (optopt == 'd') - RTE_LOG(INFO, APP, "Option -%c requires an argument\n", optopt); - else if (optopt == 'p') - RTE_LOG(INFO, APP, "Option -%c requires an argument\n", optopt); - else - RTE_LOG(INFO, APP, "Unknown option character\n"); - return -1; - default: - usage(progname); - return -1; + int c, dst_flag = 0; + + while ((c = getopt(argc, argv, "d:p:")) != -1) { + switch (c) { + case 'd': + state_info->destination = strtoul(optarg, NULL, 10); + dst_flag = 1; + RTE_LOG(INFO, APP, "Sending packets to service ID %d\n", state_info->destination); + break; + case 'p': + state_info->print_delay = strtoul(optarg, NULL, 10); + break; + case '?': + usage(progname); + if (optopt == 'd') + RTE_LOG(INFO, APP, "Option -%c requires an argument\n", optopt); + else if (optopt == 'p') + RTE_LOG(INFO, APP, "Option -%c requires an argument\n", optopt); + else + RTE_LOG(INFO, APP, "Unknown option character\n"); + return -1; + default: + usage(progname); + return -1; } - } + } - if (!dst_flag) { + if (!dst_flag) { RTE_LOG(INFO, APP, "Flow Tracker NF requires a destination NF service ID with the -d flag \n"); return -1; - } + } - return optind; + return optind; } /* @@ -206,8 +204,7 @@ do_stats_display(struct state_info *state_info) { printf("%d. Status: ", index); if (data->is_active) { printf("Active\n"); - } - else { + } else { printf("Expired\n"); } @@ -222,7 +219,7 @@ do_stats_display(struct state_info *state_info) { * if so, it calls clear_entries() to free up space. */ static int -table_add_entry(struct onvm_ft_ipv4_5tuple* key, struct state_info *state_info) { +table_add_entry(struct onvm_ft_ipv4_5tuple *key, struct state_info *state_info) { struct flow_stats *data = NULL; if (unlikely(key == NULL || state_info == NULL)) { @@ -254,7 +251,7 @@ table_add_entry(struct onvm_ft_ipv4_5tuple* key, struct state_info *state_info) * and if it doesn't, it calls table_add_entry() to add it to the table. */ static int -table_lookup_entry(struct rte_mbuf* pkt, struct state_info *state_info) { +table_lookup_entry(struct rte_mbuf *pkt, struct state_info *state_info) { struct flow_stats *data = NULL; struct onvm_ft_ipv4_5tuple key; @@ -280,7 +277,7 @@ table_lookup_entry(struct rte_mbuf* pkt, struct state_info *state_info) { } static int -callback_handler(__attribute__((unused)) struct onvm_nf_info *nf_info) { +callback_handler(__attribute__((unused)) struct onvm_nf_local_ctx *nf_local_ctx) { state_info->elapsed_cycles = rte_get_tsc_cycles(); if ((state_info->elapsed_cycles - state_info->last_cycles) / rte_get_timer_hz() > state_info->print_delay) { @@ -292,7 +289,8 @@ callback_handler(__attribute__((unused)) struct onvm_nf_info *nf_info) { } static int -packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, __attribute__((unused)) struct onvm_nf_info *nf_info) { +packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, + __attribute__((unused)) struct onvm_nf_local_ctx *nf_local_ctx) { if (!onvm_pkt_is_ipv4(pkt)) { meta->destination = state_info->destination; meta->action = ONVM_NF_ACTION_TONF; @@ -312,17 +310,33 @@ packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, __attribute__(( int main(int argc, char *argv[]) { int arg_offset; + struct onvm_nf_local_ctx *nf_local_ctx; + struct onvm_nf_function_table *nf_function_table; const char *progname = argv[0]; - if ((arg_offset = onvm_nflib_init(argc, argv, NF_TAG, &nf_info)) < 0) - return -1; + nf_local_ctx = onvm_nflib_init_nf_local_ctx(); + onvm_nflib_start_signal_handler(nf_local_ctx, NULL); + + nf_function_table = onvm_nflib_init_nf_function_table(); + nf_function_table->pkt_handler = &packet_handler; + nf_function_table->user_actions = &callback_handler; + + if ((arg_offset = onvm_nflib_init(argc, argv, NF_TAG, nf_local_ctx, nf_function_table)) < 0) { + onvm_nflib_stop(nf_local_ctx); + if (arg_offset == ONVM_SIGNAL_TERMINATION) { + printf("Exiting due to user termination\n"); + return 0; + } else { + rte_exit(EXIT_FAILURE, "Failed ONVM init\n"); + } + } argc -= arg_offset; argv += arg_offset; state_info = rte_calloc("state", 1, sizeof(struct state_info), 0); if (state_info == NULL) { - onvm_nflib_stop(nf_info); + onvm_nflib_stop(nf_local_ctx); rte_exit(EXIT_FAILURE, "Unable to initialize NF state"); } @@ -330,21 +344,22 @@ main(int argc, char *argv[]) { state_info->num_stored = 0; if (parse_app_args(argc, argv, progname) < 0) { - onvm_nflib_stop(nf_info); + onvm_nflib_stop(nf_local_ctx); rte_exit(EXIT_FAILURE, "Invalid command-line arguments"); } state_info->ft = onvm_ft_create(TBL_SIZE, sizeof(struct flow_stats)); if (state_info->ft == NULL) { - onvm_nflib_stop(nf_info); + onvm_nflib_stop(nf_local_ctx); rte_exit(EXIT_FAILURE, "Unable to create flow table"); } /*Initialize NF timer */ state_info->elapsed_cycles = rte_get_tsc_cycles(); - onvm_nflib_run_callback(nf_info, &packet_handler, &callback_handler); + onvm_nflib_run(nf_local_ctx); + onvm_nflib_stop(nf_local_ctx); printf("If we reach here, program is ending!\n"); return 0; } diff --git a/examples/load_balancer/load_balancer.c b/examples/load_balancer/load_balancer.c index 684bb07bf..09d0e0d65 100644 --- a/examples/load_balancer/load_balancer.c +++ b/examples/load_balancer/load_balancer.c @@ -5,8 +5,8 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,37 +38,37 @@ * load_balancer.c - an example Layer 3 round-robin load balancer. ********************************************************************/ -#include -#include -#include +#include +#include +#include #include #include -#include -#include +#include +#include #include -#include #include -#include +#include +#include -#include -#include -#include -#include #include +#include +#include +#include +#include #include -#include +#include +#include +#include #include #include -#include +#include #include -#include -#include -#include +#include +#include "onvm_flow_table.h" #include "onvm_nflib.h" #include "onvm_pkt_helper.h" -#include "onvm_flow_table.h" #define NF_TAG "load_balancer" #define TABLE_SIZE 65536 @@ -93,10 +93,10 @@ struct loadbalance { uint8_t server_port; uint8_t client_port; - /* config file, interface names */ - char * cfg_filename; - char * client_iface_name; - char * server_iface_name; + /* config file, interface names */ + char *cfg_filename; + char *client_iface_name; + char *server_iface_name; }; /* Struct for backend servers */ @@ -113,9 +113,6 @@ struct flow_info { int is_active; }; -/* Struct that contains information about this NF */ -struct onvm_nf_info *nf_info; - struct loadbalance *lb; /* number of package between each print */ static uint32_t print_delay = 1000000; @@ -129,7 +126,10 @@ extern struct port_info *ports; static void usage(const char *progname) { printf("Usage:\n"); - printf("%s [EAL args] -- [NF_LIB args] -- [ -c client_iface] [-s server_iface] [-f server_config] -p \n", progname); + printf( + "%s [EAL args] -- [NF_LIB args] -- [ -c client_iface] [-s server_iface] [-f server_config] -p " + "\n", + progname); printf("%s -F [EAL args] -- [NF_LIB args] -- [NF args]\n\n", progname); printf("Flags:\n"); printf(" - `-c CLIENT_IFACE` : name of the client interface\n"); @@ -144,61 +144,63 @@ usage(const char *progname) { static int parse_app_args(int argc, char *argv[], const char *progname) { int c; - + lb->cfg_filename = NULL; lb->client_iface_name = NULL; lb->server_iface_name = NULL; while ((c = getopt(argc, argv, "c:s:f:p:")) != -1) { switch (c) { - case 'c': - lb->client_iface_name = strdup(optarg); - break; - case 's': - lb->server_iface_name = strdup(optarg); - break; - case 'f': - lb->cfg_filename = strdup(optarg); - break; - case 'p': - print_delay = strtoul(optarg, NULL, 10); - break; - case '?': - usage(progname); - if (optopt == 'd') - RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); - else if (optopt == 'p') - RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); - else if (isprint(optopt)) - RTE_LOG(INFO, APP, "Unknown option `-%c'.\n", optopt); - else - RTE_LOG(INFO, APP, "Unknown option character `\\x%x'.\n", optopt); - return -1; - default: - usage(progname); - return -1; + case 'c': + lb->client_iface_name = strdup(optarg); + break; + case 's': + lb->server_iface_name = strdup(optarg); + break; + case 'f': + lb->cfg_filename = strdup(optarg); + break; + case 'p': + print_delay = strtoul(optarg, NULL, 10); + break; + case '?': + usage(progname); + if (optopt == 'd') + RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); + else if (optopt == 'p') + RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); + else if (isprint(optopt)) + RTE_LOG(INFO, APP, "Unknown option `-%c'.\n", optopt); + else + RTE_LOG(INFO, APP, "Unknown option character `\\x%x'.\n", optopt); + return -1; + default: + usage(progname); + return -1; } } if (!lb->cfg_filename) { RTE_LOG(INFO, APP, "Load balancer NF requires a backend server config file.\n"); return -1; - + } + if (!lb->client_iface_name) { RTE_LOG(INFO, APP, "Load balancer NF requires a client interface name.\n"); return -1; } + if (!lb->server_iface_name) { RTE_LOG(INFO, APP, "Load balancer NF requires a backend server interface name.\n"); return -1; - } } + } return optind; } /* - * This function parses the backend config. It takes the filename - * and fills up the backend_server array. This includes the mac and ip + * This function parses the backend config. It takes the filename + * and fills up the backend_server array. This includes the mac and ip * address of the backend servers */ static int @@ -206,9 +208,9 @@ parse_backend_config(void) { int ret, temp, i; char ip[32]; char mac[32]; - FILE * cfg; + FILE *cfg; - cfg = fopen(lb->cfg_filename, "r"); + cfg = fopen(lb->cfg_filename, "r"); if (cfg == NULL) { rte_exit(EXIT_FAILURE, "Error openning server \'%s\' config\n", lb->cfg_filename); } @@ -218,7 +220,8 @@ parse_backend_config(void) { } lb->server_count = temp; - lb->server = (struct backend_server *)rte_malloc("backend server info", sizeof(struct backend_server) * lb->server_count, 0); + lb->server = (struct backend_server *)rte_malloc("backend server info", + sizeof(struct backend_server) * lb->server_count, 0); if (lb->server == NULL) { rte_exit(EXIT_FAILURE, "Malloc failed, can't allocate server information\n"); } @@ -234,7 +237,7 @@ parse_backend_config(void) { rte_exit(EXIT_FAILURE, "Error parsing config IP address #%d\n", i); } - ret =onvm_pkt_parse_mac(mac, lb->server[i].d_addr_bytes); + ret = onvm_pkt_parse_mac(mac, lb->server[i].d_addr_bytes); if (ret < 0) { rte_exit(EXIT_FAILURE, "Error parsing config MAC address #%d\n", i); } @@ -243,12 +246,12 @@ parse_backend_config(void) { fclose(cfg); printf("\nARP config:\n"); for (i = 0; i < lb->server_count; i++) { - printf("%" PRIu8 ".%" PRIu8 ".%" PRIu8 ".%" PRIu8 " ", - lb->server[i].d_ip & 0xFF, (lb->server[i].d_ip >> 8) & 0xFF, (lb->server[i].d_ip >> 16) & 0xFF, (lb->server[i].d_ip >> 24) & 0xFF); - printf("%02x:%02x:%02x:%02x:%02x:%02x\n", - lb->server[i].d_addr_bytes[0], lb->server[i].d_addr_bytes[1], - lb->server[i].d_addr_bytes[2], lb->server[i].d_addr_bytes[3], - lb->server[i].d_addr_bytes[4], lb->server[i].d_addr_bytes[5]); + printf("%" PRIu8 ".%" PRIu8 ".%" PRIu8 ".%" PRIu8 " ", (lb->server[i].d_ip >> 24) & 0xFF, + (lb->server[i].d_ip >> 16) & 0xFF, (lb->server[i].d_ip >> 8) & 0xFF, + lb->server[i].d_ip & 0xFF); + printf("%02x:%02x:%02x:%02x:%02x:%02x\n", lb->server[i].d_addr_bytes[0], lb->server[i].d_addr_bytes[1], + lb->server[i].d_addr_bytes[2], lb->server[i].d_addr_bytes[3], lb->server[i].d_addr_bytes[4], + lb->server[i].d_addr_bytes[5]); } return ret; @@ -261,11 +264,11 @@ parse_backend_config(void) { * than one lcore enabled. */ static void -do_stats_display(struct rte_mbuf* pkt) { - const char clr[] = { 27, '[', '2', 'J', '\0' }; - const char topLeft[] = { 27, '[', '1', ';', '1', 'H', '\0' }; +do_stats_display(struct rte_mbuf *pkt) { + const char clr[] = {27, '[', '2', 'J', '\0'}; + const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0'}; static uint64_t pkt_process = 0; - struct ipv4_hdr* ip; + struct ipv4_hdr *ip; pkt_process += print_delay; @@ -276,7 +279,7 @@ do_stats_display(struct rte_mbuf* pkt) { printf("-----\n"); printf("Port : %d\n", pkt->port); printf("Size : %d\n", pkt->pkt_len); - printf("N° : %"PRIu64"\n", pkt_process); + printf("N° : %" PRIu64 "\n", pkt_process); printf("\n\n"); ip = onvm_pkt_ipv4_hdr(pkt); @@ -291,14 +294,12 @@ do_stats_display(struct rte_mbuf* pkt) { * Print flow information */ static void -print_flow_info(struct flow_info* f) { +print_flow_info(struct flow_info *f) { printf("Flow INFO\n"); printf("Destination server: %d\n", f->dest); - printf("Source mac %02x:%02x:%02x:%02x:%02x:%02x\n", - f->s_addr_bytes[0], f->s_addr_bytes[1], - f->s_addr_bytes[2], f->s_addr_bytes[3], - f->s_addr_bytes[4], f->s_addr_bytes[5]); -} + printf("Source mac %02x:%02x:%02x:%02x:%02x:%02x\n", f->s_addr_bytes[0], f->s_addr_bytes[1], f->s_addr_bytes[2], + f->s_addr_bytes[3], f->s_addr_bytes[4], f->s_addr_bytes[5]); +} /* * Parse and assign load balancer server/client interface information @@ -314,20 +315,20 @@ get_iface_inf(void) { ifr.ifr_addr.sa_family = AF_INET; /* Parse server interface */ - strncpy(ifr.ifr_name, lb->server_iface_name, IFNAMSIZ-1); + strncpy(ifr.ifr_name, lb->server_iface_name, IFNAMSIZ - 1); ioctl(fd, SIOCGIFADDR, &ifr); - lb->ip_lb_server = *(uint32_t *)(&((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr); + lb->ip_lb_server = *(uint32_t *)(&((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr); ioctl(fd, SIOCGIFHWADDR, &ifr); for (i = 0; i < ETHER_ADDR_LEN; i++) server_addr_bytes[i] = ifr.ifr_hwaddr.sa_data[i]; /* Parse client interface */ - strncpy(ifr.ifr_name, lb->client_iface_name, IFNAMSIZ-1); + strncpy(ifr.ifr_name, lb->client_iface_name, IFNAMSIZ - 1); ioctl(fd, SIOCGIFADDR, &ifr); - lb->ip_lb_client = *(uint32_t *)(&((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr); + lb->ip_lb_client = *(uint32_t *)(&((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr); ioctl(fd, SIOCGIFHWADDR, &ifr); for (i = 0; i < ETHER_ADDR_LEN; i++) @@ -346,19 +347,15 @@ get_iface_inf(void) { printf("\nLoad balancer interfaces:\n"); printf("Client iface \'%s\' ID: %d, IP: %" PRIu32 " (%" PRIu8 ".%" PRIu8 ".%" PRIu8 ".%" PRIu8 "), ", - lb->client_iface_name, lb->client_port, lb->ip_lb_client, - lb->ip_lb_client & 0xFF, (lb->ip_lb_client >> 8) & 0xFF, (lb->ip_lb_client >> 16) & 0xFF, (lb->ip_lb_client >> 24) & 0xFF); - printf("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", - client_addr_bytes[0], client_addr_bytes[1], - client_addr_bytes[2], client_addr_bytes[3], - client_addr_bytes[4], client_addr_bytes[5]); + lb->client_iface_name, lb->client_port, lb->ip_lb_client, lb->ip_lb_client & 0xFF, + (lb->ip_lb_client >> 8) & 0xFF, (lb->ip_lb_client >> 16) & 0xFF, (lb->ip_lb_client >> 24) & 0xFF); + printf("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", client_addr_bytes[0], client_addr_bytes[1], client_addr_bytes[2], + client_addr_bytes[3], client_addr_bytes[4], client_addr_bytes[5]); printf("Server iface \'%s\' ID: %d, IP: %" PRIu32 " (%" PRIu8 ".%" PRIu8 ".%" PRIu8 ".%" PRIu8 "), ", - lb->server_iface_name, lb->server_port, lb->ip_lb_server, - lb->ip_lb_server & 0xFF, (lb->ip_lb_server >> 8) & 0xFF, (lb->ip_lb_server >> 16) & 0xFF, (lb->ip_lb_server >> 24) & 0xFF); - printf("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", - server_addr_bytes[0], server_addr_bytes[1], - server_addr_bytes[2], server_addr_bytes[3], - server_addr_bytes[4], server_addr_bytes[5]); + lb->server_iface_name, lb->server_port, lb->ip_lb_server, lb->ip_lb_server & 0xFF, + (lb->ip_lb_server >> 8) & 0xFF, (lb->ip_lb_server >> 16) & 0xFF, (lb->ip_lb_server >> 24) & 0xFF); + printf("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", server_addr_bytes[0], server_addr_bytes[1], server_addr_bytes[2], + server_addr_bytes[3], server_addr_bytes[4], server_addr_bytes[5]); } /* @@ -416,7 +413,7 @@ clear_entries(void) { * if so, it calls clear_entries() to free up space. */ static int -table_add_entry(struct onvm_ft_ipv4_5tuple* key, struct flow_info **flow) { +table_add_entry(struct onvm_ft_ipv4_5tuple *key, struct flow_info **flow) { struct flow_info *data = NULL; if (unlikely(key == NULL || lb == NULL)) { @@ -439,7 +436,7 @@ table_add_entry(struct onvm_ft_ipv4_5tuple* key, struct flow_info **flow) { data->dest = lb->num_stored % lb->server_count; data->last_pkt_cycles = lb->elapsed_cycles; data->is_active = 0; - + *flow = data; return 0; @@ -451,14 +448,14 @@ table_add_entry(struct onvm_ft_ipv4_5tuple* key, struct flow_info **flow) { * and if it doesn't, it calls table_add_entry() to add it to the table. */ static int -table_lookup_entry(struct rte_mbuf* pkt, struct flow_info **flow) { +table_lookup_entry(struct rte_mbuf *pkt, struct flow_info **flow) { struct flow_info *data = NULL; struct onvm_ft_ipv4_5tuple key; if (unlikely(pkt == NULL || lb == NULL || flow == NULL)) { return -1; } - + int ret = onvm_ft_fill_key_symmetric(&key, pkt); if (ret < 0) return -1; @@ -477,7 +474,7 @@ table_lookup_entry(struct rte_mbuf* pkt, struct flow_info **flow) { } static int -callback_handler(__attribute__((unused)) struct onvm_nf_info *nf_info) { +callback_handler(__attribute__((unused)) struct onvm_nf_local_ctx *nf_local_ctx) { lb->elapsed_cycles = rte_get_tsc_cycles(); if ((lb->elapsed_cycles - lb->last_cycles) / rte_get_timer_hz() > lb->expire_time) { @@ -488,9 +485,10 @@ callback_handler(__attribute__((unused)) struct onvm_nf_info *nf_info) { } static int -packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, __attribute__((unused)) struct onvm_nf_info *nf_info) { +packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, + __attribute__((unused)) struct onvm_nf_local_ctx *nf_local_ctx) { static uint32_t counter = 0; - struct ipv4_hdr* ip; + struct ipv4_hdr *ip; struct ether_hdr *ehdr; struct flow_info *flow_info; int i, ret; @@ -546,7 +544,7 @@ packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, __attribute__(( ehdr->d_addr.addr_bytes[i] = lb->server[flow_info->dest].d_addr_bytes[i]; } - ip->dst_addr = lb->server[flow_info->dest].d_ip; + ip->dst_addr = rte_cpu_to_be_32(lb->server[flow_info->dest].d_ip); meta->destination = lb->server_port; } @@ -564,18 +562,36 @@ packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, __attribute__(( return 0; } -int main(int argc, char *argv[]) { +int +main(int argc, char *argv[]) { + struct onvm_nf_local_ctx *nf_local_ctx; + struct onvm_nf_function_table *nf_function_table; int arg_offset; const char *progname = argv[0]; - - if ((arg_offset = onvm_nflib_init(argc, argv, NF_TAG, &nf_info)) < 0) - return -1; + + nf_local_ctx = onvm_nflib_init_nf_local_ctx(); + onvm_nflib_start_signal_handler(nf_local_ctx, NULL); + + nf_function_table = onvm_nflib_init_nf_function_table(); + nf_function_table->pkt_handler = &packet_handler; + nf_function_table->user_actions = &callback_handler; + + if ((arg_offset = onvm_nflib_init(argc, argv, NF_TAG, nf_local_ctx, nf_function_table)) < 0) { + onvm_nflib_stop(nf_local_ctx); + if (arg_offset == ONVM_SIGNAL_TERMINATION) { + printf("Exiting due to user termination\n"); + return 0; + } else { + rte_exit(EXIT_FAILURE, "Failed ONVM init\n"); + } + } + argc -= arg_offset; argv += arg_offset; lb = rte_calloc("state", 1, sizeof(struct loadbalance), 0); if (lb == NULL) { - onvm_nflib_stop(nf_info); + onvm_nflib_stop(nf_local_ctx); rte_exit(EXIT_FAILURE, "Unable to initialize NF lb struct"); } @@ -584,7 +600,7 @@ int main(int argc, char *argv[]) { lb->ft = onvm_ft_create(TABLE_SIZE, sizeof(struct flow_info)); if (lb->ft == NULL) { - onvm_nflib_stop(nf_info); + onvm_nflib_stop(nf_local_ctx); rte_exit(EXIT_FAILURE, "Unable to create flow table"); } @@ -594,8 +610,9 @@ int main(int argc, char *argv[]) { lb->expire_time = 32; lb->elapsed_cycles = rte_get_tsc_cycles(); - onvm_nflib_run_callback(nf_info, &packet_handler, &callback_handler); - printf("If we reach here, program is ending\n"); + onvm_nflib_run(nf_local_ctx); + onvm_nflib_stop(nf_local_ctx); + printf("If we reach here, program is ending\n"); return 0; } diff --git a/examples/load_generator/load_generator.c b/examples/load_generator/load_generator.c index cdf5e95d1..53812df63 100644 --- a/examples/load_generator/load_generator.c +++ b/examples/load_generator/load_generator.c @@ -5,8 +5,8 @@ * BSD LICENSE * * Copyright(c) - * 2015-2018 George Washington University - * 2015-2018 University of California Riverside + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,31 +38,31 @@ * load_generator.c - send pkts at defined rate and measure received pkts. ********************************************************************/ -#include -#include -#include -#include +#include +#include #include +#include +#include #include -#include -#include +#include +#include #include -#include #include -#include +#include +#include #include -#include -#include -#include #include -#include #include #include +#include +#include +#include +#include +#include "onvm_flow_table.h" #include "onvm_nflib.h" #include "onvm_pkt_helper.h" -#include "onvm_flow_table.h" #define LOCAL_EXPERIMENTAL_ETHER 0x88B5 #define NF_TAG "load_generator" @@ -86,9 +86,6 @@ struct rte_mempool *pktmbuf_pool; static uint16_t packet_size = ETHER_HDR_LEN; static uint8_t d_addr_bytes[ETHER_ADDR_LEN]; -/* Struct that contains information about this NF */ -struct onvm_nf_info *nf_info; - /* number of seconds between each print */ static double print_delay = 0.1; static double time_since_print = 0; @@ -100,7 +97,8 @@ static uint8_t action_out = 0; static struct ether_hdr *ehdr; /* Sets up variables for the load generator */ -void nf_setup(struct onvm_nf_info *nf_info); +void +nf_setup(struct onvm_nf_local_ctx *nf_local_ctx); /* * Print a usage message @@ -108,14 +106,23 @@ void nf_setup(struct onvm_nf_info *nf_info); static void usage(const char *progname) { printf("Usage:\n"); - printf("%s [EAL args] -- [NF_LIB args] -- -d [-m ] " - "[-p ] [-s ] [-t ] [-o]\n\n", progname); + printf( + "%s [EAL args] -- [NF_LIB args] -- -d [-m ] " + "[-p ] [-s ] [-t ] [-o]\n\n", + progname); printf("%s -F [EAL args] -- [NF_LIB args] -- [NF args]\n\n", progname); printf("Flags:\n"); printf(" - `-d DST`: destination service ID to foward to, or dst port if `-o` is used.\n"); - printf(" - `-p `: number of seconds between each print (e.g. `-p 0.1` prints every 0.1 seconds).\n"); - printf(" - `-t `: the desired transmission rate for the packets (e.g. `-t 3000000 transmits 3 million packets per second). Note that the actual transmission rate may be limited based on system performance and NF configuration. If the load generator is experiencing high levels of dropped packets either transmitting or receiving, lowering the transmission rate could solve this.\n"); - printf(" - `-m `: user specified destination MAC address (e.g. `-m aa:bb:cc:dd:ee:ff` sets the destination address within the ethernet header that is located at the start of the packet data).\n"); + printf( + " - `-p `: number of seconds between each print (e.g. `-p 0.1` prints every 0.1 seconds).\n"); + printf( + " - `-t `: the desired transmission rate for the packets (e.g. `-t 3000000 transmits 3 " + "million packets per second). Note that the actual transmission rate may be limited based on system " + "performance and NF configuration. If the load generator is experiencing high levels of dropped packets " + "either transmitting or receiving, lowering the transmission rate could solve this.\n"); + printf( + " - `-m `: user specified destination MAC address (e.g. `-m aa:bb:cc:dd:ee:ff` sets the " + "destination address within the ethernet header that is located at the start of the packet data).\n"); printf(" - `-s `: the desired size of the generated packets in bytes.\n"); printf(" - `-o`: send the packets out the NIC port.\n"); } @@ -141,17 +148,11 @@ parse_app_args(int argc, char *argv[], const char *progname) { packet_rate = strtoul(optarg, NULL, 10); break; case 'm': - count = sscanf(optarg, - "%x:%x:%x:%x:%x:%x", - &values[0], - &values[1], - &values[2], - &values[3], - &values[4], - &values[5]); + count = sscanf(optarg, "%x:%x:%x:%x:%x:%x", &values[0], &values[1], &values[2], + &values[3], &values[4], &values[5]); if (count == ETHER_ADDR_LEN) { for (i = 0; i < ETHER_ADDR_LEN; ++i) { - d_addr_bytes[i] = (uint8_t) values[i]; + d_addr_bytes[i] = (uint8_t)values[i]; } } else { usage(progname); @@ -163,7 +164,8 @@ parse_app_args(int argc, char *argv[], const char *progname) { if (packet_size >= ETHER_HDR_LEN) { break; } else { - RTE_LOG(INFO, APP, "Load generator NF requires a packet size of at least 14.\n"); + RTE_LOG(INFO, APP, + "Load generator NF requires a packet size of at least 14.\n"); return -1; } case 'o': @@ -185,12 +187,12 @@ parse_app_args(int argc, char *argv[], const char *progname) { static void do_stats_display(void) { - const char clr[] = { 27, '[', '2', 'J', '\0' }; - const char topLeft[] = { 27, '[', '1', ';', '1', 'H', '\0' }; + const char clr[] = {27, '[', '2', 'J', '\0'}; + const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0'}; uint64_t cur_cycle = rte_get_tsc_cycles(); double time_elapsed = (cur_cycle - start_cycle) / (double)rte_get_timer_hz(); - double time_since_update = (cur_cycle - last_update_cycle) / (double) rte_get_timer_hz(); + double time_since_update = (cur_cycle - last_update_cycle) / (double)rte_get_timer_hz(); double tx_rate_average = packets_sent / time_elapsed; double tx_rate_current = packets_sent_since_update / time_since_update; @@ -198,7 +200,8 @@ do_stats_display(void) { double rx_rate_average = packets_received / time_elapsed; double rx_rate_current = packets_received_since_update / time_since_update; - double latency_current_mean = total_latency_since_update / (double)packets_received_since_update / (double) (rte_get_timer_hz() / 1000000); + double latency_current_mean = + total_latency_since_update / (double)packets_received_since_update / (double)(rte_get_timer_hz() / 1000000); last_update_cycle = cur_cycle; packets_sent_since_update = 0; @@ -211,14 +214,14 @@ do_stats_display(void) { printf("Time elapsed: %.2f\n", time_elapsed); printf("\n"); - printf("Tx total packets: %"PRIu64"\n", packets_sent); - printf("Tx packets sent this iteration: %"PRIu32"\n", batch_size); - printf("Tx rate (set): %"PRIu64"\n", packet_rate); + printf("Tx total packets: %" PRIu64 "\n", packets_sent); + printf("Tx packets sent this iteration: %" PRIu32 "\n", batch_size); + printf("Tx rate (set): %" PRIu64 "\n", packet_rate); printf("Tx rate (average): %.2f\n", tx_rate_average); printf("Tx rate (current): %.2f\n", tx_rate_current); printf("\n"); - printf("Rx total packets: %"PRIu64" \n", packets_received); + printf("Rx total packets: %" PRIu64 " \n", packets_received); printf("Rx rate (average): %.2f\n", rx_rate_average); printf("Rx rate (current): %.2f\n", rx_rate_current); printf("Latency (current mean): %.2f us\n", latency_current_mean); @@ -227,8 +230,9 @@ do_stats_display(void) { } static int -packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, __attribute__((unused)) struct onvm_nf_info *nf_info) { - uint64_t* timestamp; +packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, + __attribute__((unused)) struct onvm_nf_local_ctx *nf_local_ctx) { + uint64_t *timestamp; if (!ONVM_CHECK_BIT(meta->flags, LOAD_GEN_BIT)) { meta->action = ONVM_NF_ACTION_DROP; @@ -246,7 +250,7 @@ packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, __attribute__(( } static int -callback_handler(__attribute__((unused)) struct onvm_nf_info *nf_info) { +callback_handler(__attribute__((unused)) struct onvm_nf_local_ctx *nf_local_ctx) { uint32_t i; uint64_t cur_cycle = rte_get_tsc_cycles(); double time_delta = (cur_cycle - last_cycle) / (double)rte_get_timer_hz(); @@ -254,12 +258,12 @@ callback_handler(__attribute__((unused)) struct onvm_nf_info *nf_info) { last_cycle = cur_cycle; if (packets_to_send >= 1) { - batch_size = (packets_to_send <= BATCH_LIMIT) ? (int) packets_to_send : BATCH_LIMIT; + batch_size = (packets_to_send <= BATCH_LIMIT) ? (int)packets_to_send : BATCH_LIMIT; struct rte_mbuf *pkts[batch_size]; for (i = 0; i < batch_size; i++) { struct ether_hdr *pkt_ehdr; struct rte_mbuf *pkt = rte_pktmbuf_alloc(pktmbuf_pool); - uint64_t* timestamp; + uint64_t *timestamp; if (pkt == NULL) { printf("Failed to allocate packets\n"); @@ -267,10 +271,10 @@ callback_handler(__attribute__((unused)) struct onvm_nf_info *nf_info) { } /* Append and copy ether header */ - pkt_ehdr = (struct ether_hdr *) rte_pktmbuf_append(pkt, packet_size); + pkt_ehdr = (struct ether_hdr *)rte_pktmbuf_append(pkt, packet_size); rte_memcpy(pkt_ehdr, ehdr, sizeof(struct ether_hdr)); - struct onvm_pkt_meta* pmeta = onvm_get_pkt_meta(pkt); + struct onvm_pkt_meta *pmeta = onvm_get_pkt_meta(pkt); pmeta->destination = destination; pmeta->flags |= ONVM_SET_BIT(0, LOAD_GEN_BIT); if (action_out) { @@ -280,7 +284,7 @@ callback_handler(__attribute__((unused)) struct onvm_nf_info *nf_info) { } /* Add data to measure latency */ - timestamp = (uint64_t *) rte_pktmbuf_append(pkt, sizeof(uint64_t)); + timestamp = (uint64_t *)rte_pktmbuf_append(pkt, sizeof(uint64_t)); *timestamp = rte_get_tsc_cycles(); pkts[i] = pkt; @@ -289,7 +293,7 @@ callback_handler(__attribute__((unused)) struct onvm_nf_info *nf_info) { packets_sent_since_update++; packets_to_send--; } - onvm_nflib_return_pkt_bulk(nf_info, pkts, batch_size); + onvm_nflib_return_pkt_bulk(nf_local_ctx->nf, pkts, batch_size); } time_since_print += time_delta; @@ -301,12 +305,11 @@ callback_handler(__attribute__((unused)) struct onvm_nf_info *nf_info) { return 0; } - /* * Sets up load generator values */ void -nf_setup(struct onvm_nf_info *nf_info) { +nf_setup(struct onvm_nf_local_ctx *nf_local_ctx) { int j; start_cycle = rte_get_tsc_cycles(); @@ -315,11 +318,11 @@ nf_setup(struct onvm_nf_info *nf_info) { pktmbuf_pool = rte_mempool_lookup(PKTMBUF_POOL_NAME); if (pktmbuf_pool == NULL) { - onvm_nflib_stop(nf_info); + onvm_nflib_stop(nf_local_ctx); rte_exit(EXIT_FAILURE, "Cannot find mbuf pool!\n"); } - ehdr = (struct ether_hdr *) malloc(sizeof(struct ether_hdr)); + ehdr = (struct ether_hdr *)malloc(sizeof(struct ether_hdr)); if (ehdr == NULL) { rte_exit(EXIT_FAILURE, "Failed to allocate common ehdr\n"); } @@ -331,28 +334,43 @@ nf_setup(struct onvm_nf_info *nf_info) { ehdr->ether_type = rte_cpu_to_be_16(LOCAL_EXPERIMENTAL_ETHER); } -int main(int argc, char *argv[]) { +int +main(int argc, char *argv[]) { int arg_offset; - + struct onvm_nf_local_ctx *nf_local_ctx; + struct onvm_nf_function_table *nf_function_table; const char *progname = argv[0]; - if ((arg_offset = onvm_nflib_init(argc, argv, NF_TAG, &nf_info)) < 0) - return -1; + nf_local_ctx = onvm_nflib_init_nf_local_ctx(); + onvm_nflib_start_signal_handler(nf_local_ctx, NULL); + + nf_function_table = onvm_nflib_init_nf_function_table(); + nf_function_table->pkt_handler = &packet_handler; + nf_function_table->user_actions = &callback_handler; + + if ((arg_offset = onvm_nflib_init(argc, argv, NF_TAG, nf_local_ctx, nf_function_table)) < 0) { + onvm_nflib_stop(nf_local_ctx); + if (arg_offset == ONVM_SIGNAL_TERMINATION) { + printf("Exiting due to user termination\n"); + return 0; + } else { + rte_exit(EXIT_FAILURE, "Failed ONVM init\n"); + } + } + argc -= arg_offset; argv += arg_offset; if (parse_app_args(argc, argv, progname) < 0) { - onvm_nflib_stop(nf_info); + onvm_nflib_stop(nf_local_ctx); rte_exit(EXIT_FAILURE, "Invalid command-line arguments\n"); } - onvm_nflib_set_setup_function(nf_info, &nf_setup); - - onvm_nflib_run_callback(nf_info, &packet_handler, &callback_handler); + onvm_nflib_run(nf_local_ctx); free(ehdr); + onvm_nflib_stop(nf_local_ctx); printf("If we reach here, program is ending\n"); - return 0; } diff --git a/examples/ndpi_stats/ndpi_stats.c b/examples/ndpi_stats/ndpi_stats.c index 272d73435..453f71c65 100644 --- a/examples/ndpi_stats/ndpi_stats.c +++ b/examples/ndpi_stats/ndpi_stats.c @@ -5,8 +5,8 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,20 +38,20 @@ * ndpi_stats.c - an example using onvm, nDPI. Inspect packets using nDPI ********************************************************************/ -#include -#include -#include +#include +#include #include #include -#include -#include +#include +#include #include -#include #include +#include +#include #include -#include #include +#include #include #include "ndpi_main.h" @@ -63,22 +63,19 @@ #define NF_TAG "ndpi_stat" #define TICK_RESOLUTION 1000 -/* Struct that contains information about this NF */ -struct onvm_nf_info *nf_info; - /* shared data structure containing host port info */ extern struct port_info *ports; /* user defined settings */ -static uint32_t destination = (uint16_t) -1; +static uint32_t destination = (uint16_t)-1; /* pcap stucts */ -const uint16_t MAX_SNAPLEN = (uint16_t) -1; +const uint16_t MAX_SNAPLEN = (uint16_t)-1; pcap_t *pd; /* nDPI structs */ -struct ndpi_detection_module_struct * module; -struct ndpi_workflow * workflow; +struct ndpi_detection_module_struct *module; +struct ndpi_workflow *workflow; uint32_t current_ndpi_memory = 0, max_ndpi_memory = 0; static u_int8_t quiet_mode = 0; static u_int16_t decode_tunnels = 0; @@ -86,65 +83,69 @@ static FILE *results_file = NULL; static struct timeval begin, end; /* nDPI methods */ -void setup_ndpi(void); -char* formatTraffic(float numBits, int bits, char *buf); -char* formatPackets(float numPkts, char *buf); -static void node_proto_guess_walker(const void *node, ndpi_VISIT which, int depth, void *user_data); -static void print_results(void); +void +setup_ndpi(void); +char * +formatTraffic(float numBits, int bits, char *buf); +char * +formatPackets(float numPkts, char *buf); +static void +node_proto_guess_walker(const void *node, ndpi_VISIT which, int depth, void *user_data); +static void +print_results(void); /** * Source https://github.com/ntop/nDPI ndpiReader.c * @brief Traffic stats format */ -char* formatTraffic(float numBits, int bits, char *buf) { - - char unit; - - if(bits) - unit = 'b'; - else - unit = 'B'; - - if(numBits < 1024) { - snprintf(buf, 32, "%lu %c", (unsigned long)numBits, unit); - } else if(numBits < (1024*1024)) { - snprintf(buf, 32, "%.2f K%c", (float)(numBits)/1024, unit); - } else { - float tmpMBits = ((float)numBits)/(1024*1024); - - if(tmpMBits < 1024) { - snprintf(buf, 32, "%.2f M%c", tmpMBits, unit); - } else { - tmpMBits /= 1024; - - if(tmpMBits < 1024) { - snprintf(buf, 32, "%.2f G%c", tmpMBits, unit); - } else { - snprintf(buf, 32, "%.2f T%c", (float)(tmpMBits)/1024, unit); - } - } - } - - return(buf); -} +char * +formatTraffic(float numBits, int bits, char *buf) { + char unit; + + if (bits) + unit = 'b'; + else + unit = 'B'; + + if (numBits < 1024) { + snprintf(buf, 32, "%lu %c", (unsigned long)numBits, unit); + } else if (numBits < (1024 * 1024)) { + snprintf(buf, 32, "%.2f K%c", (float)(numBits) / 1024, unit); + } else { + float tmpMBits = ((float)numBits) / (1024 * 1024); + + if (tmpMBits < 1024) { + snprintf(buf, 32, "%.2f M%c", tmpMBits, unit); + } else { + tmpMBits /= 1024; + + if (tmpMBits < 1024) { + snprintf(buf, 32, "%.2f G%c", tmpMBits, unit); + } else { + snprintf(buf, 32, "%.2f T%c", (float)(tmpMBits) / 1024, unit); + } + } + } + return (buf); +} /** * Source https://github.com/ntop/nDPI ndpiReader.c * @brief Packets stats format */ -char* formatPackets(float numPkts, char *buf) { - - if(numPkts < 1000) { - snprintf(buf, 32, "%.2f", numPkts); - } else if(numPkts < (1000*1000)) { - snprintf(buf, 32, "%.2f K", numPkts/1000); - } else { - numPkts /= (1000*1000); - snprintf(buf, 32, "%.2f M", numPkts); - } - - return(buf); +char * +formatPackets(float numPkts, char *buf) { + if (numPkts < 1000) { + snprintf(buf, 32, "%.2f", numPkts); + } else if (numPkts < (1000 * 1000)) { + snprintf(buf, 32, "%.2f K", numPkts / 1000); + } else { + numPkts /= (1000 * 1000); + snprintf(buf, 32, "%.2f M", numPkts); + } + + return (buf); } /* @@ -167,7 +168,7 @@ static int parse_app_args(int argc, char *argv[], const char *progname) { int c; - while ((c = getopt (argc, argv, "d:w:")) != -1) { + while ((c = getopt(argc, argv, "d:w:")) != -1) { switch (c) { case 'w': results_file = fopen(strdup(optarg), "w"); @@ -198,7 +199,7 @@ parse_app_args(int argc, char *argv[], const char *progname) { return optind; } -void +void setup_ndpi(void) { pd = pcap_open_dead(DLT_EN10MB, MAX_SNAPLEN); @@ -225,20 +226,22 @@ setup_ndpi(void) { * Source https://github.com/ntop/nDPI ndpiReader.c * Modified for single workflow */ -static void +static void node_proto_guess_walker(const void *node, ndpi_VISIT which, int depth, void *user_data) { - struct ndpi_flow_info *flow = *(struct ndpi_flow_info **) node; - u_int16_t thread_id = *((u_int16_t *) user_data); - - if((which == ndpi_preorder) || (which == ndpi_leaf)) { /* Avoid walking the same node multiple times */ - if((!flow->detection_completed) && flow->ndpi_flow) - flow->detected_protocol = ndpi_detection_giveup(workflow->ndpi_struct, flow->ndpi_flow); - - process_ndpi_collected_info(workflow, flow); - workflow->stats.protocol_counter[flow->detected_protocol.app_protocol] += flow->src2dst_packets + flow->dst2src_packets; - workflow->stats.protocol_counter_bytes[flow->detected_protocol.app_protocol] += flow->src2dst_bytes + flow->dst2src_bytes; - workflow->stats.protocol_flows[flow->detected_protocol.app_protocol]++; - } + struct ndpi_flow_info *flow = *(struct ndpi_flow_info **)node; + u_int16_t thread_id = *((u_int16_t *)user_data); + + if ((which == ndpi_preorder) || (which == ndpi_leaf)) { /* Avoid walking the same node multiple times */ + if ((!flow->detection_completed) && flow->ndpi_flow) + flow->detected_protocol = ndpi_detection_giveup(workflow->ndpi_struct, flow->ndpi_flow); + + process_ndpi_collected_info(workflow, flow); + workflow->stats.protocol_counter[flow->detected_protocol.app_protocol] += + flow->src2dst_packets + flow->dst2src_packets; + workflow->stats.protocol_counter_bytes[flow->detected_protocol.app_protocol] += + flow->src2dst_bytes + flow->dst2src_bytes; + workflow->stats.protocol_flows[flow->detected_protocol.app_protocol]++; + } } /* @@ -251,27 +254,27 @@ print_results(void) { u_int32_t avg_pkt_size = 0; u_int64_t tot_usec; - if(workflow->stats.total_wire_bytes == 0) return; + if (workflow->stats.total_wire_bytes == 0) + return; - for(i=0; indpi_flows_root[i], node_proto_guess_walker, 0); } - tot_usec = end.tv_sec*1000000 + end.tv_usec - (begin.tv_sec*1000000 + begin.tv_usec); + tot_usec = end.tv_sec * 1000000 + end.tv_usec - (begin.tv_sec * 1000000 + begin.tv_usec); printf("\nTraffic statistics:\n"); printf("\tEthernet bytes: %-13llu (includes ethernet CRC/IFC/trailer)\n", - (long long unsigned int)workflow->stats.total_wire_bytes); - printf("\tDiscarded bytes: %-13llu\n", - (long long unsigned int)workflow->stats.total_discarded_bytes); + (long long unsigned int)workflow->stats.total_wire_bytes); + printf("\tDiscarded bytes: %-13llu\n", (long long unsigned int)workflow->stats.total_discarded_bytes); printf("\tIP packets: %-13llu of %llu packets total\n", - (long long unsigned int)workflow->stats.ip_packet_count, - (long long unsigned int)workflow->stats.raw_packet_count); + (long long unsigned int)workflow->stats.ip_packet_count, + (long long unsigned int)workflow->stats.raw_packet_count); /* In order to prevent Floating point exception in case of no traffic*/ - if(workflow->stats.total_ip_bytes && workflow->stats.raw_packet_count) - avg_pkt_size = (unsigned int)(workflow->stats.total_ip_bytes/workflow->stats.raw_packet_count); + if (workflow->stats.total_ip_bytes && workflow->stats.raw_packet_count) + avg_pkt_size = (unsigned int)(workflow->stats.total_ip_bytes / workflow->stats.raw_packet_count); printf("\tIP bytes: %-13llu (avg pkt size %u bytes)\n", - (long long unsigned int)workflow->stats.total_ip_bytes,avg_pkt_size); + (long long unsigned int)workflow->stats.total_ip_bytes, avg_pkt_size); printf("\tUnique flows: %-13u\n", workflow->stats.ndpi_flow_count); printf("\tTCP Packets: %-13lu\n", (unsigned long)workflow->stats.tcp_count); @@ -280,7 +283,7 @@ print_results(void) { printf("\tMPLS Packets: %-13lu\n", (unsigned long)workflow->stats.mpls_count); printf("\tPPPoE Packets: %-13lu\n", (unsigned long)workflow->stats.pppoe_count); printf("\tFragmented Packets: %-13lu\n", (unsigned long)workflow->stats.fragmented_count); - printf("\tMax Packet size: %-13u\n", workflow->stats.max_packet_len); + printf("\tMax Packet size: %-13u\n", workflow->stats.max_packet_len); printf("\tPacket Len < 64: %-13lu\n", (unsigned long)workflow->stats.packet_len[0]); printf("\tPacket Len 64-128: %-13lu\n", (unsigned long)workflow->stats.packet_len[1]); printf("\tPacket Len 128-256: %-13lu\n", (unsigned long)workflow->stats.packet_len[2]); @@ -288,45 +291,47 @@ print_results(void) { printf("\tPacket Len 1024-1500: %-13lu\n", (unsigned long)workflow->stats.packet_len[4]); printf("\tPacket Len > 1500: %-13lu\n", (unsigned long)workflow->stats.packet_len[5]); - if(tot_usec > 0) { + if (tot_usec > 0) { char buf[32], buf1[32], when[64]; - float t = (float)(workflow->stats.ip_packet_count*1000000)/(float)tot_usec; - float b = (float)(workflow->stats.total_wire_bytes * 8 *1000000)/(float)tot_usec; + float t = (float)(workflow->stats.ip_packet_count * 1000000) / (float)tot_usec; + float b = (float)(workflow->stats.total_wire_bytes * 8 * 1000000) / (float)tot_usec; float traffic_duration; /* This currently assumes traffic starts to flow instantly */ traffic_duration = tot_usec; printf("\tnDPI throughput: %s pps / %s/sec\n", formatPackets(t, buf), formatTraffic(b, 1, buf1)); - t = (float)(workflow->stats.ip_packet_count*1000000)/(float)traffic_duration; - b = (float)(workflow->stats.total_wire_bytes * 8 *1000000)/(float)traffic_duration; + t = (float)(workflow->stats.ip_packet_count * 1000000) / (float)traffic_duration; + b = (float)(workflow->stats.total_wire_bytes * 8 * 1000000) / (float)traffic_duration; strftime(when, sizeof(when), "%d/%b/%Y %H:%M:%S", localtime(&begin.tv_sec)); printf("\tAnalysis begin: %s\n", when); strftime(when, sizeof(when), "%d/%b/%Y %H:%M:%S", localtime(&end.tv_sec)); printf("\tAnalysis end: %s\n", when); printf("\tTraffic throughput: %s pps / %s/sec\n", formatPackets(t, buf), formatTraffic(b, 1, buf1)); - printf("\tTraffic duration: %.3f sec\n", traffic_duration/1000000); + printf("\tTraffic duration: %.3f sec\n", traffic_duration / 1000000); } for (i = 0; i <= ndpi_get_num_supported_protocols(workflow->ndpi_struct); i++) { if (workflow->stats.protocol_counter[i] > 0) { if (results_file) fprintf(results_file, "%s\t%llu\t%llu\t%u\n", - ndpi_get_proto_name(workflow->ndpi_struct, i), - (long long unsigned int)workflow->stats.protocol_counter[i], - (long long unsigned int)workflow->stats.protocol_counter_bytes[i], - workflow->stats.protocol_flows[i]); - printf("\t%-20s packets: %-13llu bytes: %-13llu " - "flows: %-13u\n", - ndpi_get_proto_name(workflow->ndpi_struct, i), - (long long unsigned int)workflow->stats.protocol_counter[i], - (long long unsigned int)workflow->stats.protocol_counter_bytes[i], - workflow->stats.protocol_flows[i]); + ndpi_get_proto_name(workflow->ndpi_struct, i), + (long long unsigned int)workflow->stats.protocol_counter[i], + (long long unsigned int)workflow->stats.protocol_counter_bytes[i], + workflow->stats.protocol_flows[i]); + printf( + "\t%-20s packets: %-13llu bytes: %-13llu " + "flows: %-13u\n", + ndpi_get_proto_name(workflow->ndpi_struct, i), + (long long unsigned int)workflow->stats.protocol_counter[i], + (long long unsigned int)workflow->stats.protocol_counter_bytes[i], + workflow->stats.protocol_flows[i]); } } } static int -packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, __attribute__((unused)) struct onvm_nf_info *nf_info) { +packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, + __attribute__((unused)) struct onvm_nf_local_ctx *nf_local_ctx) { struct pcap_pkthdr pkt_hdr; struct timeval time; u_char *packet; @@ -337,13 +342,13 @@ packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, __attribute__(( pkt_hdr.ts = time; pkt_hdr.caplen = rte_pktmbuf_data_len(pkt); pkt_hdr.len = rte_pktmbuf_data_len(pkt); - packet = rte_pktmbuf_mtod(pkt, u_char * ); + packet = rte_pktmbuf_mtod(pkt, u_char *); - prot = ndpi_workflow_process_packet(workflow, &pkt_hdr, packet); + prot = ndpi_workflow_process_packet(workflow, &pkt_hdr, packet); workflow->stats.protocol_counter[prot.app_protocol]++; workflow->stats.protocol_counter_bytes[prot.app_protocol] += pkt_hdr.len; - if (destination != (uint16_t) -1) { + if (destination != (uint16_t)-1) { meta->action = ONVM_NF_ACTION_TONF; meta->destination = destination; } else { @@ -358,17 +363,34 @@ packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, __attribute__(( return 0; } -int main(int argc, char *argv[]) { +int +main(int argc, char *argv[]) { int arg_offset; + struct onvm_nf_local_ctx *nf_local_ctx; + struct onvm_nf_function_table *nf_function_table; const char *progname = argv[0]; - if ((arg_offset = onvm_nflib_init(argc, argv, NF_TAG, &nf_info)) < 0) - return -1; + nf_local_ctx = onvm_nflib_init_nf_local_ctx(); + onvm_nflib_start_signal_handler(nf_local_ctx, NULL); + + nf_function_table = onvm_nflib_init_nf_function_table(); + nf_function_table->pkt_handler = &packet_handler; + + if ((arg_offset = onvm_nflib_init(argc, argv, NF_TAG, nf_local_ctx, nf_function_table)) < 0) { + onvm_nflib_stop(nf_local_ctx); + if (arg_offset == ONVM_SIGNAL_TERMINATION) { + printf("Exiting due to user termination\n"); + return 0; + } else { + rte_exit(EXIT_FAILURE, "Failed ONVM init\n"); + } + } + argc -= arg_offset; argv += arg_offset; if (parse_app_args(argc, argv, progname) < 0) { - onvm_nflib_stop(nf_info); + onvm_nflib_stop(nf_local_ctx); rte_exit(EXIT_FAILURE, "Invalid command-line arguments\n"); } @@ -376,7 +398,7 @@ int main(int argc, char *argv[]) { gettimeofday(&begin, NULL); - onvm_nflib_run(nf_info, &packet_handler); + onvm_nflib_run(nf_local_ctx); gettimeofday(&end, NULL); print_results(); @@ -386,6 +408,7 @@ int main(int argc, char *argv[]) { if (results_file) fclose(results_file); + onvm_nflib_stop(nf_local_ctx); printf("If we reach here, program is ending\n"); return 0; } diff --git a/examples/ndpi_stats/ndpi_util.c b/examples/ndpi_stats/ndpi_util.c index bd0f51ba8..3886f52dd 100644 --- a/examples/ndpi_stats/ndpi_util.c +++ b/examples/ndpi_stats/ndpi_util.c @@ -24,47 +24,47 @@ #include #ifdef WIN32 -#include /* winsock.h is included automatically */ -#include #include +#include +#include /* winsock.h is included automatically */ #else -#include #include +#include #endif #ifndef ETH_P_IP -#define ETH_P_IP 0x0800 /* IPv4 */ +#define ETH_P_IP 0x0800 /* IPv4 */ #endif #ifndef ETH_P_IPv6 -#define ETH_P_IPV6 0x86dd /* IPv6 */ +#define ETH_P_IPV6 0x86dd /* IPv6 */ #endif -#define SLARP 0x8035 /* Cisco Slarp */ -#define CISCO_D_PROTO 0x2000 /* Cisco Discovery Protocol */ +#define SLARP 0x8035 /* Cisco Slarp */ +#define CISCO_D_PROTO 0x2000 /* Cisco Discovery Protocol */ -#define VLAN 0x8100 -#define MPLS_UNI 0x8847 -#define MPLS_MULTI 0x8848 -#define PPPoE 0x8864 -#define SNAP 0xaa -#define BSTP 0x42 /* Bridge Spanning Tree Protocol */ +#define VLAN 0x8100 +#define MPLS_UNI 0x8847 +#define MPLS_MULTI 0x8848 +#define PPPoE 0x8864 +#define SNAP 0xaa +#define BSTP 0x42 /* Bridge Spanning Tree Protocol */ /* mask for FCF */ -#define WIFI_DATA 0x2 /* 0000 0010 */ -#define FCF_TYPE(fc) (((fc) >> 2) & 0x3) /* 0000 0011 = 0x3 */ -#define FCF_SUBTYPE(fc) (((fc) >> 4) & 0xF) /* 0000 1111 = 0xF */ -#define FCF_TO_DS(fc) ((fc) & 0x0100) -#define FCF_FROM_DS(fc) ((fc) & 0x0200) +#define WIFI_DATA 0x2 /* 0000 0010 */ +#define FCF_TYPE(fc) (((fc) >> 2) & 0x3) /* 0000 0011 = 0x3 */ +#define FCF_SUBTYPE(fc) (((fc) >> 4) & 0xF) /* 0000 1111 = 0xF */ +#define FCF_TO_DS(fc) ((fc)&0x0100) +#define FCF_FROM_DS(fc) ((fc)&0x0200) /* mask for Bad FCF presence */ -#define BAD_FCS 0x50 /* 0101 0000 */ +#define BAD_FCS 0x50 /* 0101 0000 */ -#define GTP_U_V1_PORT 2152 -#define TZSP_PORT 37008 +#define GTP_U_V1_PORT 2152 +#define TZSP_PORT 37008 #ifndef DLT_LINUX_SLL -#define DLT_LINUX_SLL 113 +#define DLT_LINUX_SLL 113 #endif #include "ndpi_main.h" @@ -72,10 +72,20 @@ /* ***************************************************** */ -void ndpi_free_flow_info_half(struct ndpi_flow_info *flow) { - if(flow->ndpi_flow) { ndpi_flow_free(flow->ndpi_flow); flow->ndpi_flow = NULL; } - if(flow->src_id) { ndpi_free(flow->src_id); flow->src_id = NULL; } - if(flow->dst_id) { ndpi_free(flow->dst_id); flow->dst_id = NULL; } +void +ndpi_free_flow_info_half(struct ndpi_flow_info *flow) { + if (flow->ndpi_flow) { + ndpi_flow_free(flow->ndpi_flow); + flow->ndpi_flow = NULL; + } + if (flow->src_id) { + ndpi_free(flow->src_id); + flow->src_id = NULL; + } + if (flow->dst_id) { + ndpi_free(flow->dst_id); + flow->dst_id = NULL; + } } /* ***************************************************** */ @@ -85,13 +95,14 @@ extern u_int32_t current_ndpi_memory, max_ndpi_memory; /** * @brief malloc wrapper function */ -static void *malloc_wrapper(size_t size) { - current_ndpi_memory += size; +static void * +malloc_wrapper(size_t size) { + current_ndpi_memory += size; - if(current_ndpi_memory > max_ndpi_memory) - max_ndpi_memory = current_ndpi_memory; + if (current_ndpi_memory > max_ndpi_memory) + max_ndpi_memory = current_ndpi_memory; - return malloc(size); + return malloc(size); } /* ***************************************************** */ @@ -99,371 +110,379 @@ static void *malloc_wrapper(size_t size) { /** * @brief free wrapper function */ -static void free_wrapper(void *freeable) { - free(freeable); +static void +free_wrapper(void *freeable) { + free(freeable); } /* ***************************************************** */ -struct ndpi_workflow * ndpi_workflow_init(const struct ndpi_workflow_prefs * prefs, pcap_t * pcap_handle) { +struct ndpi_workflow * +ndpi_workflow_init(const struct ndpi_workflow_prefs *prefs, pcap_t *pcap_handle) { + set_ndpi_malloc(malloc_wrapper), set_ndpi_free(free_wrapper); + set_ndpi_flow_malloc(NULL), set_ndpi_flow_free(NULL); + /* TODO: just needed here to init ndpi malloc wrapper */ + struct ndpi_detection_module_struct *module = ndpi_init_detection_module(); - set_ndpi_malloc(malloc_wrapper), set_ndpi_free(free_wrapper); - set_ndpi_flow_malloc(NULL), set_ndpi_flow_free(NULL); - /* TODO: just needed here to init ndpi malloc wrapper */ - struct ndpi_detection_module_struct * module = ndpi_init_detection_module(); + struct ndpi_workflow *workflow = ndpi_calloc(1, sizeof(struct ndpi_workflow)); - struct ndpi_workflow * workflow = ndpi_calloc(1, sizeof(struct ndpi_workflow)); + workflow->pcap_handle = pcap_handle; + workflow->prefs = *prefs; + workflow->ndpi_struct = module; - workflow->pcap_handle = pcap_handle; - workflow->prefs = *prefs; - workflow->ndpi_struct = module; + if (workflow->ndpi_struct == NULL) { + NDPI_LOG(0, NULL, NDPI_LOG_ERROR, "global structure initialization failed\n"); + exit(-1); + } - if(workflow->ndpi_struct == NULL) { - NDPI_LOG(0, NULL, NDPI_LOG_ERROR, "global structure initialization failed\n"); - exit(-1); - } - - workflow->ndpi_flows_root = ndpi_calloc(workflow->prefs.num_roots, sizeof(void *)); - return workflow; + workflow->ndpi_flows_root = ndpi_calloc(workflow->prefs.num_roots, sizeof(void *)); + return workflow; } /* ***************************************************** */ -void ndpi_flow_info_freer(void *node) { - struct ndpi_flow_info *flow = (struct ndpi_flow_info*)node; +void +ndpi_flow_info_freer(void *node) { + struct ndpi_flow_info *flow = (struct ndpi_flow_info *)node; - ndpi_free_flow_info_half(flow); - ndpi_free(flow); + ndpi_free_flow_info_half(flow); + ndpi_free(flow); } /* ***************************************************** */ -void ndpi_workflow_free(struct ndpi_workflow * workflow) { - int i; +void +ndpi_workflow_free(struct ndpi_workflow *workflow) { + int i; - for(i=0; iprefs.num_roots; i++) - ndpi_tdestroy(workflow->ndpi_flows_root[i], ndpi_flow_info_freer); + for (i = 0; i < workflow->prefs.num_roots; i++) + ndpi_tdestroy(workflow->ndpi_flows_root[i], ndpi_flow_info_freer); - ndpi_exit_detection_module(workflow->ndpi_struct); - free(workflow->ndpi_flows_root); - free(workflow); + ndpi_exit_detection_module(workflow->ndpi_struct); + free(workflow->ndpi_flows_root); + free(workflow); } /* ***************************************************** */ -int ndpi_workflow_node_cmp(const void *a, const void *b) { - struct ndpi_flow_info *fa = (struct ndpi_flow_info*)a; - struct ndpi_flow_info *fb = (struct ndpi_flow_info*)b; - - if(fa->hashval < fb->hashval) return(-1); else if(fa->hashval > fb->hashval) return(1); - - /* Flows have the same hash */ - - if(fa->vlan_id < fb->vlan_id ) return(-1); else { if(fa->vlan_id > fb->vlan_id ) return(1); } - if(fa->protocol < fb->protocol ) return(-1); else { if(fa->protocol > fb->protocol ) return(1); } - - if( - ( - (fa->src_ip == fb->src_ip ) - && (fa->src_port == fb->src_port) - && (fa->dst_ip == fb->dst_ip ) - && (fa->dst_port == fb->dst_port) - ) - || - ( - (fa->src_ip == fb->dst_ip ) - && (fa->src_port == fb->dst_port) - && (fa->dst_ip == fb->src_ip ) - && (fa->dst_port == fb->src_port) - ) - ) - return(0); - - if(fa->src_ip < fb->src_ip ) return(-1); else { if(fa->src_ip > fb->src_ip ) return(1); } - if(fa->src_port < fb->src_port) return(-1); else { if(fa->src_port > fb->src_port) return(1); } - if(fa->dst_ip < fb->dst_ip ) return(-1); else { if(fa->dst_ip > fb->dst_ip ) return(1); } - if(fa->dst_port < fb->dst_port) return(-1); else { if(fa->dst_port > fb->dst_port) return(1); } - - return(0); /* notreached */ +int +ndpi_workflow_node_cmp(const void *a, const void *b) { + struct ndpi_flow_info *fa = (struct ndpi_flow_info *)a; + struct ndpi_flow_info *fb = (struct ndpi_flow_info *)b; + + if (fa->hashval < fb->hashval) + return (-1); + else if (fa->hashval > fb->hashval) + return (1); + + /* Flows have the same hash */ + + if (fa->vlan_id < fb->vlan_id) + return (-1); + else { + if (fa->vlan_id > fb->vlan_id) + return (1); + } + if (fa->protocol < fb->protocol) + return (-1); + else { + if (fa->protocol > fb->protocol) + return (1); + } + + if (((fa->src_ip == fb->src_ip) && (fa->src_port == fb->src_port) && (fa->dst_ip == fb->dst_ip) && + (fa->dst_port == fb->dst_port)) || + ((fa->src_ip == fb->dst_ip) && (fa->src_port == fb->dst_port) && (fa->dst_ip == fb->src_ip) && + (fa->dst_port == fb->src_port))) + return (0); + + if (fa->src_ip < fb->src_ip) + return (-1); + else { + if (fa->src_ip > fb->src_ip) + return (1); + } + if (fa->src_port < fb->src_port) + return (-1); + else { + if (fa->src_port > fb->src_port) + return (1); + } + if (fa->dst_ip < fb->dst_ip) + return (-1); + else { + if (fa->dst_ip > fb->dst_ip) + return (1); + } + if (fa->dst_port < fb->dst_port) + return (-1); + else { + if (fa->dst_port > fb->dst_port) + return (1); + } + + return (0); /* notreached */ } /* ***************************************************** */ -static void patchIPv6Address(char *str) { - int i = 0, j = 0; - - while(str[i] != '\0') { - if((str[i] == ':') - && (str[i+1] == '0') - && (str[i+2] == ':')) { - str[j++] = ':'; - str[j++] = ':'; - i += 3; - } else - str[j++] = str[i++]; - } - if(str[j] != '\0') str[j] = '\0'; +static void +patchIPv6Address(char *str) { + int i = 0, j = 0; + + while (str[i] != '\0') { + if ((str[i] == ':') && (str[i + 1] == '0') && (str[i + 2] == ':')) { + str[j++] = ':'; + str[j++] = ':'; + i += 3; + } else + str[j++] = str[i++]; + } + if (str[j] != '\0') + str[j] = '\0'; } /* ***************************************************** */ -static struct ndpi_flow_info *get_ndpi_flow_info(struct ndpi_workflow * workflow, - const u_int8_t version, - u_int16_t vlan_id, - const struct ndpi_iphdr *iph, - const struct ndpi_ipv6hdr *iph6, - u_int16_t ip_offset, - u_int16_t ipsize, - u_int16_t l4_packet_len, - struct ndpi_tcphdr **tcph, - struct ndpi_udphdr **udph, - u_int16_t *sport, u_int16_t *dport, - struct ndpi_id_struct **src, - struct ndpi_id_struct **dst, - u_int8_t *proto, - u_int8_t **payload, - u_int16_t *payload_len, - u_int8_t *src_to_dst_direction) { - u_int32_t idx, l4_offset, hashval; - struct ndpi_flow_info flow; - void *ret; - u_int8_t *l3, *l4; - - /* - Note: to keep things simple (ndpiReader is just a demo app) - we handle IPv6 a-la-IPv4. - */ - if(version == IPVERSION) { - if(ipsize < 20) - return NULL; - - if((iph->ihl * 4) > ipsize || ipsize < ntohs(iph->tot_len) - /* || (iph->frag_off & htons(0x1FFF)) != 0 */) - return NULL; - - l4_offset = iph->ihl * 4; - l3 = (u_int8_t*)iph; - } else { - l4_offset = sizeof(struct ndpi_ipv6hdr); - l3 = (u_int8_t*)iph6; - } - - if(l4_packet_len < 64) - workflow->stats.packet_len[0]++; - else if(l4_packet_len >= 64 && l4_packet_len < 128) - workflow->stats.packet_len[1]++; - else if(l4_packet_len >= 128 && l4_packet_len < 256) - workflow->stats.packet_len[2]++; - else if(l4_packet_len >= 256 && l4_packet_len < 1024) - workflow->stats.packet_len[3]++; - else if(l4_packet_len >= 1024 && l4_packet_len < 1500) - workflow->stats.packet_len[4]++; - else if(l4_packet_len >= 1500) - workflow->stats.packet_len[5]++; - - if(l4_packet_len > workflow->stats.max_packet_len) - workflow->stats.max_packet_len = l4_packet_len; - - *proto = iph->protocol; - l4 = ((u_int8_t *) l3 + l4_offset); - - if(iph->protocol == IPPROTO_TCP && l4_packet_len >= 20) { - u_int tcp_len; - - // tcp - workflow->stats.tcp_count++; - *tcph = (struct ndpi_tcphdr *)l4; - *sport = ntohs((*tcph)->source), *dport = ntohs((*tcph)->dest); - tcp_len = ndpi_min(4*(*tcph)->doff, l4_packet_len); - *payload = &l4[tcp_len]; - *payload_len = ndpi_max(0, l4_packet_len-4*(*tcph)->doff); - } else if(iph->protocol == IPPROTO_UDP && l4_packet_len >= 8) { - // udp - - workflow->stats.udp_count++; - *udph = (struct ndpi_udphdr *)l4; - *sport = ntohs((*udph)->source), *dport = ntohs((*udph)->dest); - *payload = &l4[sizeof(struct ndpi_udphdr)]; - *payload_len = ndpi_max(0, l4_packet_len-sizeof(struct ndpi_udphdr)); - } else { - // non tcp/udp protocols - *sport = *dport = 0; - } - - flow.protocol = iph->protocol, flow.vlan_id = vlan_id; - flow.src_ip = iph->saddr, flow.dst_ip = iph->daddr; - flow.src_port = htons(*sport), flow.dst_port = htons(*dport); - flow.hashval = hashval = flow.protocol + flow.vlan_id + flow.src_ip + flow.dst_ip + flow.src_port + flow.dst_port; - idx = hashval % workflow->prefs.num_roots; - ret = ndpi_tfind(&flow, &workflow->ndpi_flows_root[idx], ndpi_workflow_node_cmp); - - if(ret == NULL) { - if(workflow->stats.ndpi_flow_count == workflow->prefs.max_ndpi_flows) { - NDPI_LOG(0, workflow->ndpi_struct, NDPI_LOG_ERROR, - "maximum flow count (%u) has been exceeded\n", - workflow->prefs.max_ndpi_flows); - exit(-1); - } else { - struct ndpi_flow_info *newflow = (struct ndpi_flow_info*)malloc(sizeof(struct ndpi_flow_info)); - - if(newflow == NULL) { - NDPI_LOG(0, workflow->ndpi_struct, NDPI_LOG_ERROR, "[NDPI] %s(1): not enough memory\n", __FUNCTION__); - return(NULL); - } - - memset(newflow, 0, sizeof(struct ndpi_flow_info)); - newflow->hashval = hashval; - newflow->protocol = iph->protocol, newflow->vlan_id = vlan_id; - newflow->src_ip = iph->saddr, newflow->dst_ip = iph->daddr; - newflow->src_port = htons(*sport), newflow->dst_port = htons(*dport); - newflow->ip_version = version; - - if(version == IPVERSION) { - inet_ntop(AF_INET, &newflow->src_ip, newflow->src_name, sizeof(newflow->src_name)); - inet_ntop(AF_INET, &newflow->dst_ip, newflow->dst_name, sizeof(newflow->dst_name)); - } else { - inet_ntop(AF_INET6, &iph6->ip6_src, newflow->src_name, sizeof(newflow->src_name)); - inet_ntop(AF_INET6, &iph6->ip6_dst, newflow->dst_name, sizeof(newflow->dst_name)); - /* For consistency across platforms replace :0: with :: */ - patchIPv6Address(newflow->src_name), patchIPv6Address(newflow->dst_name); - } - - if((newflow->ndpi_flow = ndpi_flow_malloc(SIZEOF_FLOW_STRUCT)) == NULL) { - NDPI_LOG(0, workflow->ndpi_struct, NDPI_LOG_ERROR, "[NDPI] %s(2): not enough memory\n", __FUNCTION__); - free(newflow); - return(NULL); - } else - memset(newflow->ndpi_flow, 0, SIZEOF_FLOW_STRUCT); - - if((newflow->src_id = ndpi_malloc(SIZEOF_ID_STRUCT)) == NULL) { - NDPI_LOG(0, workflow->ndpi_struct, NDPI_LOG_ERROR, "[NDPI] %s(3): not enough memory\n", __FUNCTION__); - free(newflow); - return(NULL); - } else - memset(newflow->src_id, 0, SIZEOF_ID_STRUCT); - - if((newflow->dst_id = ndpi_malloc(SIZEOF_ID_STRUCT)) == NULL) { - NDPI_LOG(0, workflow->ndpi_struct, NDPI_LOG_ERROR, "[NDPI] %s(4): not enough memory\n", __FUNCTION__); - free(newflow); - return(NULL); - } else - memset(newflow->dst_id, 0, SIZEOF_ID_STRUCT); - - ndpi_tsearch(newflow, &workflow->ndpi_flows_root[idx], ndpi_workflow_node_cmp); /* Add */ - workflow->stats.ndpi_flow_count++; - - *src = newflow->src_id, *dst = newflow->dst_id; - - return newflow; - } - } else { - struct ndpi_flow_info *flow = *(struct ndpi_flow_info**)ret; - - if(flow->src_ip == iph->saddr - && flow->dst_ip == iph->daddr - && flow->src_port == htons(*sport) - && flow->dst_port == htons(*dport) - ) - *src = flow->src_id, *dst = flow->dst_id, *src_to_dst_direction = 1; - else - *src = flow->dst_id, *dst = flow->src_id, *src_to_dst_direction = 0, flow->bidirectional = 1; - - return flow; - } +static struct ndpi_flow_info * +get_ndpi_flow_info(struct ndpi_workflow *workflow, const u_int8_t version, u_int16_t vlan_id, + const struct ndpi_iphdr *iph, const struct ndpi_ipv6hdr *iph6, u_int16_t ip_offset, u_int16_t ipsize, + u_int16_t l4_packet_len, struct ndpi_tcphdr **tcph, struct ndpi_udphdr **udph, u_int16_t *sport, + u_int16_t *dport, struct ndpi_id_struct **src, struct ndpi_id_struct **dst, u_int8_t *proto, + u_int8_t **payload, u_int16_t *payload_len, u_int8_t *src_to_dst_direction) { + u_int32_t idx, l4_offset, hashval; + struct ndpi_flow_info flow; + void *ret; + u_int8_t *l3, *l4; + + /* + Note: to keep things simple (ndpiReader is just a demo app) + we handle IPv6 a-la-IPv4. + */ + if (version == IPVERSION) { + if (ipsize < 20) + return NULL; + + if ((iph->ihl * 4) > ipsize || ipsize < ntohs(iph->tot_len) + /* || (iph->frag_off & htons(0x1FFF)) != 0 */) + return NULL; + + l4_offset = iph->ihl * 4; + l3 = (u_int8_t *)iph; + } else { + l4_offset = sizeof(struct ndpi_ipv6hdr); + l3 = (u_int8_t *)iph6; + } + + if (l4_packet_len < 64) + workflow->stats.packet_len[0]++; + else if (l4_packet_len >= 64 && l4_packet_len < 128) + workflow->stats.packet_len[1]++; + else if (l4_packet_len >= 128 && l4_packet_len < 256) + workflow->stats.packet_len[2]++; + else if (l4_packet_len >= 256 && l4_packet_len < 1024) + workflow->stats.packet_len[3]++; + else if (l4_packet_len >= 1024 && l4_packet_len < 1500) + workflow->stats.packet_len[4]++; + else if (l4_packet_len >= 1500) + workflow->stats.packet_len[5]++; + + if (l4_packet_len > workflow->stats.max_packet_len) + workflow->stats.max_packet_len = l4_packet_len; + + *proto = iph->protocol; + l4 = ((u_int8_t *)l3 + l4_offset); + + if (iph->protocol == IPPROTO_TCP && l4_packet_len >= 20) { + u_int tcp_len; + + // tcp + workflow->stats.tcp_count++; + *tcph = (struct ndpi_tcphdr *)l4; + *sport = ntohs((*tcph)->source), *dport = ntohs((*tcph)->dest); + tcp_len = ndpi_min(4 * (*tcph)->doff, l4_packet_len); + *payload = &l4[tcp_len]; + *payload_len = ndpi_max(0, l4_packet_len - 4 * (*tcph)->doff); + } else if (iph->protocol == IPPROTO_UDP && l4_packet_len >= 8) { + // udp + + workflow->stats.udp_count++; + *udph = (struct ndpi_udphdr *)l4; + *sport = ntohs((*udph)->source), *dport = ntohs((*udph)->dest); + *payload = &l4[sizeof(struct ndpi_udphdr)]; + *payload_len = ndpi_max(0, l4_packet_len - sizeof(struct ndpi_udphdr)); + } else { + // non tcp/udp protocols + *sport = *dport = 0; + } + + flow.protocol = iph->protocol, flow.vlan_id = vlan_id; + flow.src_ip = iph->saddr, flow.dst_ip = iph->daddr; + flow.src_port = htons(*sport), flow.dst_port = htons(*dport); + flow.hashval = hashval = + flow.protocol + flow.vlan_id + flow.src_ip + flow.dst_ip + flow.src_port + flow.dst_port; + idx = hashval % workflow->prefs.num_roots; + ret = ndpi_tfind(&flow, &workflow->ndpi_flows_root[idx], ndpi_workflow_node_cmp); + + if (ret == NULL) { + if (workflow->stats.ndpi_flow_count == workflow->prefs.max_ndpi_flows) { + NDPI_LOG(0, workflow->ndpi_struct, NDPI_LOG_ERROR, + "maximum flow count (%u) has been exceeded\n", workflow->prefs.max_ndpi_flows); + exit(-1); + } else { + struct ndpi_flow_info *newflow = (struct ndpi_flow_info *)malloc(sizeof(struct ndpi_flow_info)); + + if (newflow == NULL) { + NDPI_LOG(0, workflow->ndpi_struct, NDPI_LOG_ERROR, "[NDPI] %s(1): not enough memory\n", + __FUNCTION__); + return (NULL); + } + + memset(newflow, 0, sizeof(struct ndpi_flow_info)); + newflow->hashval = hashval; + newflow->protocol = iph->protocol, newflow->vlan_id = vlan_id; + newflow->src_ip = iph->saddr, newflow->dst_ip = iph->daddr; + newflow->src_port = htons(*sport), newflow->dst_port = htons(*dport); + newflow->ip_version = version; + + if (version == IPVERSION) { + inet_ntop(AF_INET, &newflow->src_ip, newflow->src_name, sizeof(newflow->src_name)); + inet_ntop(AF_INET, &newflow->dst_ip, newflow->dst_name, sizeof(newflow->dst_name)); + } else { + inet_ntop(AF_INET6, &iph6->ip6_src, newflow->src_name, sizeof(newflow->src_name)); + inet_ntop(AF_INET6, &iph6->ip6_dst, newflow->dst_name, sizeof(newflow->dst_name)); + /* For consistency across platforms replace :0: with :: */ + patchIPv6Address(newflow->src_name), patchIPv6Address(newflow->dst_name); + } + + if ((newflow->ndpi_flow = ndpi_flow_malloc(SIZEOF_FLOW_STRUCT)) == NULL) { + NDPI_LOG(0, workflow->ndpi_struct, NDPI_LOG_ERROR, "[NDPI] %s(2): not enough memory\n", + __FUNCTION__); + free(newflow); + return (NULL); + } else + memset(newflow->ndpi_flow, 0, SIZEOF_FLOW_STRUCT); + + if ((newflow->src_id = ndpi_malloc(SIZEOF_ID_STRUCT)) == NULL) { + NDPI_LOG(0, workflow->ndpi_struct, NDPI_LOG_ERROR, "[NDPI] %s(3): not enough memory\n", + __FUNCTION__); + free(newflow); + return (NULL); + } else + memset(newflow->src_id, 0, SIZEOF_ID_STRUCT); + + if ((newflow->dst_id = ndpi_malloc(SIZEOF_ID_STRUCT)) == NULL) { + NDPI_LOG(0, workflow->ndpi_struct, NDPI_LOG_ERROR, "[NDPI] %s(4): not enough memory\n", + __FUNCTION__); + free(newflow); + return (NULL); + } else + memset(newflow->dst_id, 0, SIZEOF_ID_STRUCT); + + ndpi_tsearch(newflow, &workflow->ndpi_flows_root[idx], ndpi_workflow_node_cmp); /* Add */ + workflow->stats.ndpi_flow_count++; + + *src = newflow->src_id, *dst = newflow->dst_id; + + return newflow; + } + } else { + struct ndpi_flow_info *flow = *(struct ndpi_flow_info **)ret; + + if (flow->src_ip == iph->saddr && flow->dst_ip == iph->daddr && flow->src_port == htons(*sport) && + flow->dst_port == htons(*dport)) + *src = flow->src_id, *dst = flow->dst_id, *src_to_dst_direction = 1; + else + *src = flow->dst_id, *dst = flow->src_id, *src_to_dst_direction = 0, flow->bidirectional = 1; + + return flow; + } } /* ****************************************************** */ -static struct ndpi_flow_info *get_ndpi_flow_info6(struct ndpi_workflow * workflow, - u_int16_t vlan_id, - const struct ndpi_ipv6hdr *iph6, - u_int16_t ip_offset, - struct ndpi_tcphdr **tcph, - struct ndpi_udphdr **udph, - u_int16_t *sport, u_int16_t *dport, - struct ndpi_id_struct **src, - struct ndpi_id_struct **dst, - u_int8_t *proto, - u_int8_t **payload, - u_int16_t *payload_len, - u_int8_t *src_to_dst_direction) { - struct ndpi_iphdr iph; - - memset(&iph, 0, sizeof(iph)); - iph.version = IPVERSION; - iph.saddr = iph6->ip6_src.u6_addr.u6_addr32[2] + iph6->ip6_src.u6_addr.u6_addr32[3]; - iph.daddr = iph6->ip6_dst.u6_addr.u6_addr32[2] + iph6->ip6_dst.u6_addr.u6_addr32[3]; - iph.protocol = iph6->ip6_ctlun.ip6_un1.ip6_un1_nxt; - - if(iph.protocol == IPPROTO_DSTOPTS /* IPv6 destination option */) { - u_int8_t *options = (u_int8_t*)iph6 + sizeof(const struct ndpi_ipv6hdr); - - iph.protocol = options[0]; - } - - return(get_ndpi_flow_info(workflow, 6, vlan_id, &iph, iph6, ip_offset, - sizeof(struct ndpi_ipv6hdr), - ntohs(iph6->ip6_ctlun.ip6_un1.ip6_un1_plen), - tcph, udph, sport, dport, - src, dst, proto, payload, payload_len, src_to_dst_direction)); +static struct ndpi_flow_info * +get_ndpi_flow_info6(struct ndpi_workflow *workflow, u_int16_t vlan_id, const struct ndpi_ipv6hdr *iph6, + u_int16_t ip_offset, struct ndpi_tcphdr **tcph, struct ndpi_udphdr **udph, u_int16_t *sport, + u_int16_t *dport, struct ndpi_id_struct **src, struct ndpi_id_struct **dst, u_int8_t *proto, + u_int8_t **payload, u_int16_t *payload_len, u_int8_t *src_to_dst_direction) { + struct ndpi_iphdr iph; + + memset(&iph, 0, sizeof(iph)); + iph.version = IPVERSION; + iph.saddr = iph6->ip6_src.u6_addr.u6_addr32[2] + iph6->ip6_src.u6_addr.u6_addr32[3]; + iph.daddr = iph6->ip6_dst.u6_addr.u6_addr32[2] + iph6->ip6_dst.u6_addr.u6_addr32[3]; + iph.protocol = iph6->ip6_ctlun.ip6_un1.ip6_un1_nxt; + + if (iph.protocol == IPPROTO_DSTOPTS /* IPv6 destination option */) { + u_int8_t *options = (u_int8_t *)iph6 + sizeof(const struct ndpi_ipv6hdr); + + iph.protocol = options[0]; + } + + return (get_ndpi_flow_info(workflow, 6, vlan_id, &iph, iph6, ip_offset, sizeof(struct ndpi_ipv6hdr), + ntohs(iph6->ip6_ctlun.ip6_un1.ip6_un1_plen), tcph, udph, sport, dport, src, dst, + proto, payload, payload_len, src_to_dst_direction)); } /* ****************************************************** */ -void process_ndpi_collected_info(struct ndpi_workflow * workflow, struct ndpi_flow_info *flow) { - if(!flow->ndpi_flow) return; - - snprintf(flow->host_server_name, sizeof(flow->host_server_name), "%s", - flow->ndpi_flow->host_server_name); - - /* BITTORRENT */ - if(flow->detected_protocol.app_protocol == NDPI_PROTOCOL_BITTORRENT) { - int i, j, n = 0; - - for(i=0, j = 0; j < sizeof(flow->bittorent_hash)-1; i++) { - sprintf(&flow->bittorent_hash[j], "%02x", flow->ndpi_flow->bittorent_hash[i]); - j += 2, n += flow->ndpi_flow->bittorent_hash[i]; - } - - if(n == 0) flow->bittorent_hash[0] = '\0'; - } - /* MDNS */ - else if(flow->detected_protocol.app_protocol == NDPI_PROTOCOL_MDNS) { - snprintf(flow->info, sizeof(flow->info), "%s", flow->ndpi_flow->protos.mdns.answer); - } - /* UBNTAC2 */ - else if(flow->detected_protocol.app_protocol == NDPI_PROTOCOL_UBNTAC2) { - snprintf(flow->info, sizeof(flow->info), "%s", flow->ndpi_flow->protos.ubntac2.version); - } - if(flow->detected_protocol.app_protocol != NDPI_PROTOCOL_DNS) { - /* SSH */ - if(flow->detected_protocol.app_protocol == NDPI_PROTOCOL_SSH) { - snprintf(flow->ssh_ssl.client_info, sizeof(flow->ssh_ssl.client_info), "%s", - flow->ndpi_flow->protos.ssh.client_signature); - snprintf(flow->ssh_ssl.server_info, sizeof(flow->ssh_ssl.server_info), "%s", - flow->ndpi_flow->protos.ssh.server_signature); - } - /* SSL */ - else if((flow->detected_protocol.app_protocol == NDPI_PROTOCOL_SSL) - || (flow->detected_protocol.master_protocol == NDPI_PROTOCOL_SSL)) { - snprintf(flow->ssh_ssl.client_info, sizeof(flow->ssh_ssl.client_info), "%s", - flow->ndpi_flow->protos.ssl.client_certificate); - snprintf(flow->ssh_ssl.server_info, sizeof(flow->ssh_ssl.server_info), "%s", - flow->ndpi_flow->protos.ssl.server_certificate); - } - } - - if(flow->detection_completed) { - if(flow->detected_protocol.app_protocol == NDPI_PROTOCOL_UNKNOWN) { - if (workflow->__flow_giveup_callback != NULL) - workflow->__flow_giveup_callback(workflow, flow, workflow->__flow_giveup_udata); - } else { - if (workflow->__flow_detected_callback != NULL) - workflow->__flow_detected_callback(workflow, flow, workflow->__flow_detected_udata); - } - - ndpi_free_flow_info_half(flow); - } +void +process_ndpi_collected_info(struct ndpi_workflow *workflow, struct ndpi_flow_info *flow) { + if (!flow->ndpi_flow) + return; + + snprintf(flow->host_server_name, sizeof(flow->host_server_name), "%s", flow->ndpi_flow->host_server_name); + + /* BITTORRENT */ + if (flow->detected_protocol.app_protocol == NDPI_PROTOCOL_BITTORRENT) { + int i, j, n = 0; + + for (i = 0, j = 0; j < sizeof(flow->bittorent_hash) - 1; i++) { + sprintf(&flow->bittorent_hash[j], "%02x", flow->ndpi_flow->bittorent_hash[i]); + j += 2, n += flow->ndpi_flow->bittorent_hash[i]; + } + + if (n == 0) + flow->bittorent_hash[0] = '\0'; + } + /* MDNS */ + else if (flow->detected_protocol.app_protocol == NDPI_PROTOCOL_MDNS) { + snprintf(flow->info, sizeof(flow->info), "%s", flow->ndpi_flow->protos.mdns.answer); + } + /* UBNTAC2 */ + else if (flow->detected_protocol.app_protocol == NDPI_PROTOCOL_UBNTAC2) { + snprintf(flow->info, sizeof(flow->info), "%s", flow->ndpi_flow->protos.ubntac2.version); + } + if (flow->detected_protocol.app_protocol != NDPI_PROTOCOL_DNS) { + /* SSH */ + if (flow->detected_protocol.app_protocol == NDPI_PROTOCOL_SSH) { + snprintf(flow->ssh_ssl.client_info, sizeof(flow->ssh_ssl.client_info), "%s", + flow->ndpi_flow->protos.ssh.client_signature); + snprintf(flow->ssh_ssl.server_info, sizeof(flow->ssh_ssl.server_info), "%s", + flow->ndpi_flow->protos.ssh.server_signature); + } + /* SSL */ + else if ((flow->detected_protocol.app_protocol == NDPI_PROTOCOL_SSL) || + (flow->detected_protocol.master_protocol == NDPI_PROTOCOL_SSL)) { + snprintf(flow->ssh_ssl.client_info, sizeof(flow->ssh_ssl.client_info), "%s", + flow->ndpi_flow->protos.ssl.client_certificate); + snprintf(flow->ssh_ssl.server_info, sizeof(flow->ssh_ssl.server_info), "%s", + flow->ndpi_flow->protos.ssl.server_certificate); + } + } + + if (flow->detection_completed) { + if (flow->detected_protocol.app_protocol == NDPI_PROTOCOL_UNKNOWN) { + if (workflow->__flow_giveup_callback != NULL) + workflow->__flow_giveup_callback(workflow, flow, workflow->__flow_giveup_udata); + } else { + if (workflow->__flow_detected_callback != NULL) + workflow->__flow_detected_callback(workflow, flow, workflow->__flow_detected_udata); + } + + ndpi_free_flow_info_half(flow); + } } /* ****************************************************** */ @@ -475,436 +494,431 @@ void process_ndpi_collected_info(struct ndpi_workflow * workflow, struct ndpi_fl @Note: ipsize = header->len - ip_offset ; rawsize = header->len */ -static struct ndpi_proto packet_processing(struct ndpi_workflow * workflow, - const u_int64_t time, - u_int16_t vlan_id, - const struct ndpi_iphdr *iph, - struct ndpi_ipv6hdr *iph6, - u_int16_t ip_offset, - u_int16_t ipsize, u_int16_t rawsize) { - struct ndpi_id_struct *src, *dst; - struct ndpi_flow_info *flow = NULL; - struct ndpi_flow_struct *ndpi_flow = NULL; - u_int8_t proto; - struct ndpi_tcphdr *tcph = NULL; - struct ndpi_udphdr *udph = NULL; - u_int16_t sport, dport, payload_len; - u_int8_t *payload; - u_int8_t src_to_dst_direction = 1; - u_int8_t guess = 0; - struct ndpi_proto nproto = { NDPI_PROTOCOL_UNKNOWN, NDPI_PROTOCOL_UNKNOWN }; - - if(iph) - flow = get_ndpi_flow_info(workflow, IPVERSION, vlan_id, iph, NULL, - ip_offset, ipsize, - ntohs(iph->tot_len) - (iph->ihl * 4), - &tcph, &udph, &sport, &dport, - &src, &dst, &proto, - &payload, &payload_len, &src_to_dst_direction); - else - flow = get_ndpi_flow_info6(workflow, vlan_id, iph6, ip_offset, - &tcph, &udph, &sport, &dport, - &src, &dst, &proto, - &payload, &payload_len, &src_to_dst_direction); - - if(flow != NULL) { - workflow->stats.ip_packet_count++; - workflow->stats.total_wire_bytes += rawsize + 24 /* CRC etc */, - workflow->stats.total_ip_bytes += rawsize; - ndpi_flow = flow->ndpi_flow; - - if(src_to_dst_direction) - flow->src2dst_packets++, flow->src2dst_bytes += rawsize; - else - flow->dst2src_packets++, flow->dst2src_bytes += rawsize; - - flow->last_seen = time; - } else { // flow is NULL - workflow->stats.total_discarded_bytes++; - return(nproto); - } - - /* Protocol already detected */ - if(flow->detection_completed) return(flow->detected_protocol); - - flow->detected_protocol = ndpi_detection_process_packet(workflow->ndpi_struct, ndpi_flow, - iph ? (uint8_t *)iph : (uint8_t *)iph6, - ipsize, time, src, dst); - - if((flow->detected_protocol.app_protocol != NDPI_PROTOCOL_UNKNOWN) - || ((proto == IPPROTO_UDP) && ((flow->src2dst_packets + flow->dst2src_packets) > 8)) - || ((proto == IPPROTO_TCP) && ((flow->src2dst_packets + flow->dst2src_packets) > 10))) { - /* New protocol detected or give up */ - flow->detection_completed = 1; - - if(flow->detected_protocol.app_protocol == NDPI_PROTOCOL_UNKNOWN) - flow->detected_protocol = ndpi_detection_giveup(workflow->ndpi_struct, - flow->ndpi_flow); - process_ndpi_collected_info(workflow, flow); - } - - if (guess && flow->detected_protocol.app_protocol == NDPI_PROTOCOL_UNKNOWN) - flow->detected_protocol = ndpi_guess_undetected_protocol(workflow->ndpi_struct, - flow->protocol, - ntohl(flow->src_ip), - ntohs(flow->src_port), - ntohl(flow->dst_ip), - ntohs(flow->dst_port)); - /*printf("Guess = %d\n",guess);*/ - - - - return(flow->detected_protocol); +static struct ndpi_proto +packet_processing(struct ndpi_workflow *workflow, const u_int64_t time, u_int16_t vlan_id, const struct ndpi_iphdr *iph, + struct ndpi_ipv6hdr *iph6, u_int16_t ip_offset, u_int16_t ipsize, u_int16_t rawsize) { + struct ndpi_id_struct *src, *dst; + struct ndpi_flow_info *flow = NULL; + struct ndpi_flow_struct *ndpi_flow = NULL; + u_int8_t proto; + struct ndpi_tcphdr *tcph = NULL; + struct ndpi_udphdr *udph = NULL; + u_int16_t sport, dport, payload_len; + u_int8_t *payload; + u_int8_t src_to_dst_direction = 1; + u_int8_t guess = 0; + struct ndpi_proto nproto = {NDPI_PROTOCOL_UNKNOWN, NDPI_PROTOCOL_UNKNOWN}; + + if (iph) + flow = get_ndpi_flow_info(workflow, IPVERSION, vlan_id, iph, NULL, ip_offset, ipsize, + ntohs(iph->tot_len) - (iph->ihl * 4), &tcph, &udph, &sport, &dport, &src, + &dst, &proto, &payload, &payload_len, &src_to_dst_direction); + else + flow = get_ndpi_flow_info6(workflow, vlan_id, iph6, ip_offset, &tcph, &udph, &sport, &dport, &src, &dst, + &proto, &payload, &payload_len, &src_to_dst_direction); + + if (flow != NULL) { + workflow->stats.ip_packet_count++; + workflow->stats.total_wire_bytes += rawsize + 24 /* CRC etc */, + workflow->stats.total_ip_bytes += rawsize; + ndpi_flow = flow->ndpi_flow; + + if (src_to_dst_direction) + flow->src2dst_packets++, flow->src2dst_bytes += rawsize; + else + flow->dst2src_packets++, flow->dst2src_bytes += rawsize; + + flow->last_seen = time; + } else { // flow is NULL + workflow->stats.total_discarded_bytes++; + return (nproto); + } + + /* Protocol already detected */ + if (flow->detection_completed) + return (flow->detected_protocol); + + flow->detected_protocol = ndpi_detection_process_packet( + workflow->ndpi_struct, ndpi_flow, iph ? (uint8_t *)iph : (uint8_t *)iph6, ipsize, time, src, dst); + + if ((flow->detected_protocol.app_protocol != NDPI_PROTOCOL_UNKNOWN) || + ((proto == IPPROTO_UDP) && ((flow->src2dst_packets + flow->dst2src_packets) > 8)) || + ((proto == IPPROTO_TCP) && ((flow->src2dst_packets + flow->dst2src_packets) > 10))) { + /* New protocol detected or give up */ + flow->detection_completed = 1; + + if (flow->detected_protocol.app_protocol == NDPI_PROTOCOL_UNKNOWN) + flow->detected_protocol = ndpi_detection_giveup(workflow->ndpi_struct, flow->ndpi_flow); + process_ndpi_collected_info(workflow, flow); + } + + if (guess && flow->detected_protocol.app_protocol == NDPI_PROTOCOL_UNKNOWN) + flow->detected_protocol = + ndpi_guess_undetected_protocol(workflow->ndpi_struct, flow->protocol, ntohl(flow->src_ip), + ntohs(flow->src_port), ntohl(flow->dst_ip), ntohs(flow->dst_port)); + /*printf("Guess = %d\n",guess);*/ + + return (flow->detected_protocol); } /* ****************************************************** */ -struct ndpi_proto ndpi_workflow_process_packet (struct ndpi_workflow * workflow, - const struct pcap_pkthdr *header, - const u_char *packet) { - /* - * Declare pointers to packet headers - */ - /* --- Ethernet header --- */ - const struct ndpi_ethhdr *ethernet; - /* --- LLC header --- */ - const struct ndpi_llc_header_snap *llc; - - /* --- Cisco HDLC header --- */ - const struct ndpi_chdlc *chdlc; - - /* --- Radio Tap header --- */ - const struct ndpi_radiotap_header *radiotap; - /* --- Wifi header --- */ - const struct ndpi_wifi_header *wifi; - - /* --- MPLS header --- */ - struct ndpi_mpls_header *mpls; - - /** --- IP header --- **/ - struct ndpi_iphdr *iph; - /** --- IPv6 header --- **/ - struct ndpi_ipv6hdr *iph6; - - struct ndpi_proto nproto = { NDPI_PROTOCOL_UNKNOWN, NDPI_PROTOCOL_UNKNOWN }; - - /* lengths and offsets */ - u_int16_t eth_offset = 0; - u_int16_t radio_len; - u_int16_t fc; - u_int16_t type = 0; - int wifi_len = 0; - int pyld_eth_len = 0; - int check; - u_int64_t time; - u_int16_t ip_offset = 0, ip_len; - u_int16_t frag_off = 0, vlan_id = 0; - u_int8_t proto = 0; - u_int32_t label; - - /* counters */ - u_int8_t vlan_packet = 0; - - /* Increment raw packet counter */ - workflow->stats.raw_packet_count++; - - /* setting time */ - time = ((uint64_t) header->ts.tv_sec) * TICK_RESOLUTION + header->ts.tv_usec / (1000000 / TICK_RESOLUTION); - - /* safety check */ - if(workflow->last_time > time) { - /* printf("\nWARNING: timestamp bug in the pcap file (ts delta: %llu, repairing)\n", ndpi_thread_info[thread_id].last_time - time); */ - time = workflow->last_time; - } - /* update last time value */ - workflow->last_time = time; - - /*** check Data Link type ***/ - const int datalink_type = pcap_datalink(workflow->pcap_handle); - - datalink_check: - switch(datalink_type) { - case DLT_NULL: - if(ntohl(*((u_int32_t*)&packet[eth_offset])) == 2) - type = ETH_P_IP; - else - type = ETH_P_IPV6; - - ip_offset = 4 + eth_offset; - break; - - /* Cisco PPP in HDLC-like framing - 50 */ - case DLT_PPP_SERIAL: - chdlc = (struct ndpi_chdlc *) &packet[eth_offset]; - ip_offset = sizeof(struct ndpi_chdlc); /* CHDLC_OFF = 4 */ - type = ntohs(chdlc->proto_code); - break; - - /* Cisco PPP with HDLC framing - 104 */ - case DLT_C_HDLC: - chdlc = (struct ndpi_chdlc *) &packet[eth_offset]; - ip_offset = sizeof(struct ndpi_chdlc); /* CHDLC_OFF = 4 */ - type = ntohs(chdlc->proto_code); - break; - - /* IEEE 802.3 Ethernet - 1 */ - case DLT_EN10MB: - ethernet = (struct ndpi_ethhdr *) &packet[eth_offset]; - ip_offset = sizeof(struct ndpi_ethhdr) + eth_offset; - check = ntohs(ethernet->h_proto); - - if(check <= 1500) - pyld_eth_len = check; - else if (check >= 1536) - type = check; - - if(pyld_eth_len != 0) { - llc = (struct ndpi_llc_header_snap *)(&packet[ip_offset]); - /* check for LLC layer with SNAP extension */ - if(llc->dsap == SNAP || llc->ssap == SNAP) { - type = llc->snap.proto_ID; - ip_offset += + 8; - } - /* No SNAP extension - Spanning Tree pkt must be discarted */ - else if(llc->dsap == BSTP || llc->ssap == BSTP) { - goto v4_warning; - } - } - break; - - /* Linux Cooked Capture - 113 */ - case DLT_LINUX_SLL: - type = (packet[eth_offset+14] << 8) + packet[eth_offset+15]; - ip_offset = 16 + eth_offset; - break; - - /* Radiotap link-layer - 127 */ - case DLT_IEEE802_11_RADIO: - radiotap = (struct ndpi_radiotap_header *) &packet[eth_offset]; - radio_len = radiotap->len; - - /* Check Bad FCS presence */ - if((radiotap->flags & BAD_FCS) == BAD_FCS) { - workflow->stats.total_discarded_bytes += header->len; - return(nproto); - } - - /* Calculate 802.11 header length (variable) */ - wifi = (struct ndpi_wifi_header*)( packet + eth_offset + radio_len); - fc = wifi->fc; - - /* check wifi data presence */ - if(FCF_TYPE(fc) == WIFI_DATA) { - if((FCF_TO_DS(fc) && FCF_FROM_DS(fc) == 0x0) || - (FCF_TO_DS(fc) == 0x0 && FCF_FROM_DS(fc))) - wifi_len = 26; /* + 4 byte fcs */ - } else /* no data frames */ - break; - - /* Check ether_type from LLC */ - llc = (struct ndpi_llc_header_snap*)(packet + eth_offset + wifi_len + radio_len); - if(llc->dsap == SNAP) - type = ntohs(llc->snap.proto_ID); - - /* Set IP header offset */ - ip_offset = wifi_len + radio_len + sizeof(struct ndpi_llc_header_snap) + eth_offset; - break; - - case DLT_RAW: - ip_offset = eth_offset = 0; - break; - - default: - /* printf("Unknown datalink %d\n", datalink_type); */ - return(nproto); - } - - /* check ether type */ - switch(type) { - case VLAN: - vlan_id = ((packet[ip_offset] << 8) + packet[ip_offset+1]) & 0xFFF; - type = (packet[ip_offset+2] << 8) + packet[ip_offset+3]; - ip_offset += 4; - vlan_packet = 1; - // double tagging for 802.1Q - if(type == 0x8100) { - vlan_id = ((packet[ip_offset] << 8) + packet[ip_offset+1]) & 0xFFF; - type = (packet[ip_offset+2] << 8) + packet[ip_offset+3]; - ip_offset += 4; - } - break; - case MPLS_UNI: - case MPLS_MULTI: - mpls = (struct ndpi_mpls_header *) &packet[ip_offset]; - label = ntohl(mpls->label); - /* label = ntohl(*((u_int32_t*)&packet[ip_offset])); */ - workflow->stats.mpls_count++; - type = ETH_P_IP, ip_offset += 4; - - while((label & 0x100) != 0x100) { - ip_offset += 4; - label = ntohl(mpls->label); - } - break; - case PPPoE: - workflow->stats.pppoe_count++; - type = ETH_P_IP; - ip_offset += 8; - break; - default: - break; - } - - workflow->stats.vlan_count += vlan_packet; - - iph_check: - /* Check and set IP header size and total packet length */ - iph = (struct ndpi_iphdr *) &packet[ip_offset]; - - /* just work on Ethernet packets that contain IP */ - if(type == ETH_P_IP && header->caplen >= ip_offset) { - frag_off = ntohs(iph->frag_off); - - proto = iph->protocol; - if(header->caplen < header->len) { - static u_int8_t cap_warning_used = 0; - - if(cap_warning_used == 0) { - if(!workflow->prefs.quiet_mode) - NDPI_LOG(0, workflow->ndpi_struct, NDPI_LOG_DEBUG, "\n\nWARNING: packet capture size is smaller than packet size, DETECTION MIGHT NOT WORK CORRECTLY\n\n"); - cap_warning_used = 1; - } - } - } - - if(iph->version == IPVERSION) { - ip_len = ((u_int16_t)iph->ihl * 4); - iph6 = NULL; - - if(iph->protocol == IPPROTO_IPV6) { - ip_offset += ip_len; - goto iph_check; - } - - if((frag_off & 0x1FFF) != 0) { - static u_int8_t ipv4_frags_warning_used = 0; - workflow->stats.fragmented_count++; - - if(ipv4_frags_warning_used == 0) { - if(!workflow->prefs.quiet_mode) - NDPI_LOG(0, workflow->ndpi_struct, NDPI_LOG_DEBUG, "\n\nWARNING: IPv4 fragments are not handled by this demo (nDPI supports them)\n"); - ipv4_frags_warning_used = 1; - } - - workflow->stats.total_discarded_bytes += header->len; - return(nproto); - } - } else if(iph->version == 6) { - iph6 = (struct ndpi_ipv6hdr *)&packet[ip_offset]; - proto = iph6->ip6_ctlun.ip6_un1.ip6_un1_nxt; - ip_len = sizeof(struct ndpi_ipv6hdr); - - if(proto == IPPROTO_DSTOPTS /* IPv6 destination option */) { - - u_int8_t *options = (u_int8_t*)&packet[ip_offset+ip_len]; - proto = options[0]; - ip_len += 8 * (options[1] + 1); - } - iph = NULL; - - } else { - static u_int8_t ipv4_warning_used = 0; - - v4_warning: - if(ipv4_warning_used == 0) { - if(!workflow->prefs.quiet_mode) - NDPI_LOG(0, workflow->ndpi_struct, NDPI_LOG_DEBUG, "\n\nWARNING: only IPv4/IPv6 packets are supported in this demo (nDPI supports both IPv4 and IPv6), all other packets will be discarded\n\n"); - ipv4_warning_used = 1; - } - workflow->stats.total_discarded_bytes += header->len; - return(nproto); - } - - if(workflow->prefs.decode_tunnels && (proto == IPPROTO_UDP)) { - struct ndpi_udphdr *udp = (struct ndpi_udphdr *)&packet[ip_offset+ip_len]; - u_int16_t sport = ntohs(udp->source), dport = ntohs(udp->dest); - - if((sport == GTP_U_V1_PORT) || (dport == GTP_U_V1_PORT)) { - /* Check if it's GTPv1 */ - u_int offset = ip_offset+ip_len+sizeof(struct ndpi_udphdr); - u_int8_t flags = packet[offset]; - u_int8_t message_type = packet[offset+1]; - - if((((flags & 0xE0) >> 5) == 1 /* GTPv1 */) && - (message_type == 0xFF /* T-PDU */)) { - - ip_offset = ip_offset+ip_len+sizeof(struct ndpi_udphdr)+8; /* GTPv1 header len */ - if(flags & 0x04) ip_offset += 1; /* next_ext_header is present */ - if(flags & 0x02) ip_offset += 4; /* sequence_number is present (it also includes next_ext_header and pdu_number) */ - if(flags & 0x01) ip_offset += 1; /* pdu_number is present */ - - iph = (struct ndpi_iphdr *) &packet[ip_offset]; - - if(iph->version != IPVERSION) { - // printf("WARNING: not good (packet_id=%u)!\n", (unsigned int)workflow->stats.raw_packet_count); - goto v4_warning; - } - } - } else if((sport == TZSP_PORT) || (dport == TZSP_PORT)) { - /* https://en.wikipedia.org/wiki/TZSP */ - u_int offset = ip_offset+ip_len+sizeof(struct ndpi_udphdr); - u_int8_t version = packet[offset]; - u_int8_t type = packet[offset+1]; - u_int16_t encapsulates = ntohs(*((u_int16_t*)&packet[offset+2])); - - if((version == 1) && (type == 0) && (encapsulates == 1)) { - u_int8_t stop = 0; - - offset += 4; - - while((!stop) && (offset < header->caplen)) { - u_int8_t tag_type = packet[offset]; - u_int8_t tag_len; - - switch(tag_type) { - case 0: /* PADDING Tag */ - tag_len = 1; - break; - case 1: /* END Tag */ - tag_len = 1, stop = 1; - break; - default: - tag_len = packet[offset+1]; - break; - } - - offset += tag_len; - - if(offset >= header->caplen) - return(nproto); /* Invalid packet */ - else { - eth_offset = offset; - goto datalink_check; - } - } - } - } - } - - /* process the packet */ - return(packet_processing(workflow, time, vlan_id, iph, iph6, - ip_offset, header->caplen - ip_offset, header->caplen)); +struct ndpi_proto +ndpi_workflow_process_packet(struct ndpi_workflow *workflow, const struct pcap_pkthdr *header, const u_char *packet) { + /* + * Declare pointers to packet headers + */ + /* --- Ethernet header --- */ + const struct ndpi_ethhdr *ethernet; + /* --- LLC header --- */ + const struct ndpi_llc_header_snap *llc; + + /* --- Cisco HDLC header --- */ + const struct ndpi_chdlc *chdlc; + + /* --- Radio Tap header --- */ + const struct ndpi_radiotap_header *radiotap; + /* --- Wifi header --- */ + const struct ndpi_wifi_header *wifi; + + /* --- MPLS header --- */ + struct ndpi_mpls_header *mpls; + + /** --- IP header --- **/ + struct ndpi_iphdr *iph; + /** --- IPv6 header --- **/ + struct ndpi_ipv6hdr *iph6; + + struct ndpi_proto nproto = {NDPI_PROTOCOL_UNKNOWN, NDPI_PROTOCOL_UNKNOWN}; + + /* lengths and offsets */ + u_int16_t eth_offset = 0; + u_int16_t radio_len; + u_int16_t fc; + u_int16_t type = 0; + int wifi_len = 0; + int pyld_eth_len = 0; + int check; + u_int64_t time; + u_int16_t ip_offset = 0, ip_len; + u_int16_t frag_off = 0, vlan_id = 0; + u_int8_t proto = 0; + u_int32_t label; + + /* counters */ + u_int8_t vlan_packet = 0; + + /* Increment raw packet counter */ + workflow->stats.raw_packet_count++; + + /* setting time */ + time = ((uint64_t)header->ts.tv_sec) * TICK_RESOLUTION + header->ts.tv_usec / (1000000 / TICK_RESOLUTION); + + /* safety check */ + if (workflow->last_time > time) { + /* printf("\nWARNING: timestamp bug in the pcap file (ts delta: %llu, repairing)\n", + * ndpi_thread_info[thread_id].last_time - time); */ + time = workflow->last_time; + } + /* update last time value */ + workflow->last_time = time; + + /*** check Data Link type ***/ + const int datalink_type = pcap_datalink(workflow->pcap_handle); + +datalink_check: + switch (datalink_type) { + case DLT_NULL: + if (ntohl(*((u_int32_t *)&packet[eth_offset])) == 2) + type = ETH_P_IP; + else + type = ETH_P_IPV6; + + ip_offset = 4 + eth_offset; + break; + + /* Cisco PPP in HDLC-like framing - 50 */ + case DLT_PPP_SERIAL: + chdlc = (struct ndpi_chdlc *)&packet[eth_offset]; + ip_offset = sizeof(struct ndpi_chdlc); /* CHDLC_OFF = 4 */ + type = ntohs(chdlc->proto_code); + break; + + /* Cisco PPP with HDLC framing - 104 */ + case DLT_C_HDLC: + chdlc = (struct ndpi_chdlc *)&packet[eth_offset]; + ip_offset = sizeof(struct ndpi_chdlc); /* CHDLC_OFF = 4 */ + type = ntohs(chdlc->proto_code); + break; + + /* IEEE 802.3 Ethernet - 1 */ + case DLT_EN10MB: + ethernet = (struct ndpi_ethhdr *)&packet[eth_offset]; + ip_offset = sizeof(struct ndpi_ethhdr) + eth_offset; + check = ntohs(ethernet->h_proto); + + if (check <= 1500) + pyld_eth_len = check; + else if (check >= 1536) + type = check; + + if (pyld_eth_len != 0) { + llc = (struct ndpi_llc_header_snap *)(&packet[ip_offset]); + /* check for LLC layer with SNAP extension */ + if (llc->dsap == SNAP || llc->ssap == SNAP) { + type = llc->snap.proto_ID; + ip_offset += +8; + } + /* No SNAP extension - Spanning Tree pkt must be discarted */ + else if (llc->dsap == BSTP || llc->ssap == BSTP) { + goto v4_warning; + } + } + break; + + /* Linux Cooked Capture - 113 */ + case DLT_LINUX_SLL: + type = (packet[eth_offset + 14] << 8) + packet[eth_offset + 15]; + ip_offset = 16 + eth_offset; + break; + + /* Radiotap link-layer - 127 */ + case DLT_IEEE802_11_RADIO: + radiotap = (struct ndpi_radiotap_header *)&packet[eth_offset]; + radio_len = radiotap->len; + + /* Check Bad FCS presence */ + if ((radiotap->flags & BAD_FCS) == BAD_FCS) { + workflow->stats.total_discarded_bytes += header->len; + return (nproto); + } + + /* Calculate 802.11 header length (variable) */ + wifi = (struct ndpi_wifi_header *)(packet + eth_offset + radio_len); + fc = wifi->fc; + + /* check wifi data presence */ + if (FCF_TYPE(fc) == WIFI_DATA) { + if ((FCF_TO_DS(fc) && FCF_FROM_DS(fc) == 0x0) || + (FCF_TO_DS(fc) == 0x0 && FCF_FROM_DS(fc))) + wifi_len = 26; /* + 4 byte fcs */ + } else /* no data frames */ + break; + + /* Check ether_type from LLC */ + llc = (struct ndpi_llc_header_snap *)(packet + eth_offset + wifi_len + radio_len); + if (llc->dsap == SNAP) + type = ntohs(llc->snap.proto_ID); + + /* Set IP header offset */ + ip_offset = wifi_len + radio_len + sizeof(struct ndpi_llc_header_snap) + eth_offset; + break; + + case DLT_RAW: + ip_offset = eth_offset = 0; + break; + + default: + /* printf("Unknown datalink %d\n", datalink_type); */ + return (nproto); + } + + /* check ether type */ + switch (type) { + case VLAN: + vlan_id = ((packet[ip_offset] << 8) + packet[ip_offset + 1]) & 0xFFF; + type = (packet[ip_offset + 2] << 8) + packet[ip_offset + 3]; + ip_offset += 4; + vlan_packet = 1; + // double tagging for 802.1Q + if (type == 0x8100) { + vlan_id = ((packet[ip_offset] << 8) + packet[ip_offset + 1]) & 0xFFF; + type = (packet[ip_offset + 2] << 8) + packet[ip_offset + 3]; + ip_offset += 4; + } + break; + case MPLS_UNI: + case MPLS_MULTI: + mpls = (struct ndpi_mpls_header *)&packet[ip_offset]; + label = ntohl(mpls->label); + /* label = ntohl(*((u_int32_t*)&packet[ip_offset])); */ + workflow->stats.mpls_count++; + type = ETH_P_IP, ip_offset += 4; + + while ((label & 0x100) != 0x100) { + ip_offset += 4; + label = ntohl(mpls->label); + } + break; + case PPPoE: + workflow->stats.pppoe_count++; + type = ETH_P_IP; + ip_offset += 8; + break; + default: + break; + } + + workflow->stats.vlan_count += vlan_packet; + +iph_check: + /* Check and set IP header size and total packet length */ + iph = (struct ndpi_iphdr *)&packet[ip_offset]; + + /* just work on Ethernet packets that contain IP */ + if (type == ETH_P_IP && header->caplen >= ip_offset) { + frag_off = ntohs(iph->frag_off); + + proto = iph->protocol; + if (header->caplen < header->len) { + static u_int8_t cap_warning_used = 0; + + if (cap_warning_used == 0) { + if (!workflow->prefs.quiet_mode) + NDPI_LOG(0, workflow->ndpi_struct, NDPI_LOG_DEBUG, + "\n\nWARNING: packet capture size is smaller than packet size, " + "DETECTION MIGHT NOT WORK CORRECTLY\n\n"); + cap_warning_used = 1; + } + } + } + + if (iph->version == IPVERSION) { + ip_len = ((u_int16_t)iph->ihl * 4); + iph6 = NULL; + + if (iph->protocol == IPPROTO_IPV6) { + ip_offset += ip_len; + goto iph_check; + } + + if ((frag_off & 0x1FFF) != 0) { + static u_int8_t ipv4_frags_warning_used = 0; + workflow->stats.fragmented_count++; + + if (ipv4_frags_warning_used == 0) { + if (!workflow->prefs.quiet_mode) + NDPI_LOG(0, workflow->ndpi_struct, NDPI_LOG_DEBUG, + "\n\nWARNING: IPv4 fragments are not handled by this demo (nDPI " + "supports them)\n"); + ipv4_frags_warning_used = 1; + } + + workflow->stats.total_discarded_bytes += header->len; + return (nproto); + } + } else if (iph->version == 6) { + iph6 = (struct ndpi_ipv6hdr *)&packet[ip_offset]; + proto = iph6->ip6_ctlun.ip6_un1.ip6_un1_nxt; + ip_len = sizeof(struct ndpi_ipv6hdr); + + if (proto == IPPROTO_DSTOPTS /* IPv6 destination option */) { + u_int8_t *options = (u_int8_t *)&packet[ip_offset + ip_len]; + proto = options[0]; + ip_len += 8 * (options[1] + 1); + } + iph = NULL; + + } else { + static u_int8_t ipv4_warning_used = 0; + + v4_warning: + if (ipv4_warning_used == 0) { + if (!workflow->prefs.quiet_mode) + NDPI_LOG(0, workflow->ndpi_struct, NDPI_LOG_DEBUG, + "\n\nWARNING: only IPv4/IPv6 packets are supported in this demo (nDPI " + "supports both IPv4 and IPv6), all other packets will be discarded\n\n"); + ipv4_warning_used = 1; + } + workflow->stats.total_discarded_bytes += header->len; + return (nproto); + } + + if (workflow->prefs.decode_tunnels && (proto == IPPROTO_UDP)) { + struct ndpi_udphdr *udp = (struct ndpi_udphdr *)&packet[ip_offset + ip_len]; + u_int16_t sport = ntohs(udp->source), dport = ntohs(udp->dest); + + if ((sport == GTP_U_V1_PORT) || (dport == GTP_U_V1_PORT)) { + /* Check if it's GTPv1 */ + u_int offset = ip_offset + ip_len + sizeof(struct ndpi_udphdr); + u_int8_t flags = packet[offset]; + u_int8_t message_type = packet[offset + 1]; + + if ((((flags & 0xE0) >> 5) == 1 /* GTPv1 */) && (message_type == 0xFF /* T-PDU */)) { + ip_offset = ip_offset + ip_len + sizeof(struct ndpi_udphdr) + 8; /* GTPv1 header len */ + if (flags & 0x04) + ip_offset += 1; /* next_ext_header is present */ + if (flags & 0x02) + ip_offset += + 4; /* sequence_number is present (it also includes next_ext_header and + pdu_number) */ + if (flags & 0x01) + ip_offset += 1; /* pdu_number is present */ + + iph = (struct ndpi_iphdr *)&packet[ip_offset]; + + if (iph->version != IPVERSION) { + // printf("WARNING: not good (packet_id=%u)!\n", (unsigned + // int)workflow->stats.raw_packet_count); + goto v4_warning; + } + } + } else if ((sport == TZSP_PORT) || (dport == TZSP_PORT)) { + /* https://en.wikipedia.org/wiki/TZSP */ + u_int offset = ip_offset + ip_len + sizeof(struct ndpi_udphdr); + u_int8_t version = packet[offset]; + u_int8_t type = packet[offset + 1]; + u_int16_t encapsulates = ntohs(*((u_int16_t *)&packet[offset + 2])); + + if ((version == 1) && (type == 0) && (encapsulates == 1)) { + u_int8_t stop = 0; + + offset += 4; + + while ((!stop) && (offset < header->caplen)) { + u_int8_t tag_type = packet[offset]; + u_int8_t tag_len; + + switch (tag_type) { + case 0: /* PADDING Tag */ + tag_len = 1; + break; + case 1: /* END Tag */ + tag_len = 1, stop = 1; + break; + default: + tag_len = packet[offset + 1]; + break; + } + + offset += tag_len; + + if (offset >= header->caplen) + return (nproto); /* Invalid packet */ + else { + eth_offset = offset; + goto datalink_check; + } + } + } + } + } + + /* process the packet */ + return (packet_processing(workflow, time, vlan_id, iph, iph6, ip_offset, header->caplen - ip_offset, + header->caplen)); } /* ********************************************************** */ /* http://home.thep.lu.se/~bjorn/crc/crc32_fast.c */ /* ********************************************************** */ -static uint32_t crc32_for_byte(uint32_t r) { - int j; - for(j = 0; j < 8; ++j) - r = (r & 1? 0: (uint32_t)0xEDB88320L) ^ r >> 1; - return r ^ (uint32_t)0xFF000000L; +static uint32_t +crc32_for_byte(uint32_t r) { + int j; + for (j = 0; j < 8; ++j) + r = (r & 1 ? 0 : (uint32_t)0xEDB88320L) ^ r >> 1; + return r ^ (uint32_t)0xFF000000L; } /* Any unsigned integer type with at least 32 bits may be used as @@ -912,36 +926,38 @@ static uint32_t crc32_for_byte(uint32_t r) { * probably the optimal choice for most systems. */ typedef unsigned long accum_t; -static void init_tables(uint32_t* table, uint32_t* wtable) { - size_t i, j, k, w; - for(i = 0; i < 0x100; ++i) - table[i] = crc32_for_byte(i); - for(k = 0; k < sizeof(accum_t); ++k) - for(i = 0; i < 0x100; ++i) { - for(j = w = 0; j < sizeof(accum_t); ++j) - w = table[(uint8_t)(j == k? w ^ i: w)] ^ w >> 8; - wtable[(k << 8) + i] = w ^ (k? wtable[0]: 0); - } +static void +init_tables(uint32_t *table, uint32_t *wtable) { + size_t i, j, k, w; + for (i = 0; i < 0x100; ++i) + table[i] = crc32_for_byte(i); + for (k = 0; k < sizeof(accum_t); ++k) + for (i = 0; i < 0x100; ++i) { + for (j = w = 0; j < sizeof(accum_t); ++j) + w = table[(uint8_t)(j == k ? w ^ i : w)] ^ w >> 8; + wtable[(k << 8) + i] = w ^ (k ? wtable[0] : 0); + } } -static void __crc32(const void* data, size_t n_bytes, uint32_t* crc) { - static uint32_t table[0x100], wtable[0x100*sizeof(accum_t)]; - size_t n_accum = n_bytes/sizeof(accum_t); - size_t i, j; - if(!*table) - init_tables(table, wtable); - for(i = 0; i < n_accum; ++i) { - accum_t a = *crc ^ ((accum_t*)data)[i]; - for(j = *crc = 0; j < sizeof(accum_t); ++j) - *crc ^= wtable[(j << 8) + (uint8_t)(a >> 8*j)]; - } - for(i = n_accum*sizeof(accum_t); i < n_bytes; ++i) - *crc = table[(uint8_t)*crc ^ ((uint8_t*)data)[i]] ^ *crc >> 8; +static void +__crc32(const void *data, size_t n_bytes, uint32_t *crc) { + static uint32_t table[0x100], wtable[0x100 * sizeof(accum_t)]; + size_t n_accum = n_bytes / sizeof(accum_t); + size_t i, j; + if (!*table) + init_tables(table, wtable); + for (i = 0; i < n_accum; ++i) { + accum_t a = *crc ^ ((accum_t *)data)[i]; + for (j = *crc = 0; j < sizeof(accum_t); ++j) + *crc ^= wtable[(j << 8) + (uint8_t)(a >> 8 * j)]; + } + for (i = n_accum * sizeof(accum_t); i < n_bytes; ++i) + *crc = table[(uint8_t)*crc ^ ((uint8_t *)data)[i]] ^ *crc >> 8; } -u_int32_t ethernet_crc32(const void* data, size_t n_bytes) { - u_int32_t crc = 0; - __crc32(data, n_bytes, &crc); - return crc; +u_int32_t +ethernet_crc32(const void *data, size_t n_bytes) { + u_int32_t crc = 0; + __crc32(data, n_bytes, &crc); + return crc; } - diff --git a/examples/ndpi_stats/ndpi_util.h b/examples/ndpi_stats/ndpi_util.h index 6bed00854..5e3b981e2 100644 --- a/examples/ndpi_stats/ndpi_util.h +++ b/examples/ndpi_stats/ndpi_util.h @@ -31,140 +31,142 @@ #include -#define MAX_NUM_READER_THREADS 16 -#define IDLE_SCAN_PERIOD 10 /* msec (use TICK_RESOLUTION = 1000) */ -#define MAX_IDLE_TIME 30000 -#define IDLE_SCAN_BUDGET 1024 -#define NUM_ROOTS 512 -#define MAX_NDPI_FLOWS 200000000 -#define TICK_RESOLUTION 1000 -#define MAX_NUM_IP_ADDRESS 5 /* len of ip address array */ -#define UPDATED_TREE 1 -#define AGGRESSIVE_PERCENT 95.00 -#define DIR_SRC 10 -#define DIR_DST 20 +#define MAX_NUM_READER_THREADS 16 +#define IDLE_SCAN_PERIOD 10 /* msec (use TICK_RESOLUTION = 1000) */ +#define MAX_IDLE_TIME 30000 +#define IDLE_SCAN_BUDGET 1024 +#define NUM_ROOTS 512 +#define MAX_NDPI_FLOWS 200000000 +#define TICK_RESOLUTION 1000 +#define MAX_NUM_IP_ADDRESS 5 /* len of ip address array */ +#define UPDATED_TREE 1 +#define AGGRESSIVE_PERCENT 95.00 +#define DIR_SRC 10 +#define DIR_DST 20 // flow tracking typedef struct ndpi_flow_info { - u_int32_t hashval; - u_int32_t src_ip; - u_int32_t dst_ip; - u_int16_t src_port; - u_int16_t dst_port; - u_int8_t detection_completed, protocol, bidirectional; - u_int16_t vlan_id; - struct ndpi_flow_struct *ndpi_flow; - char src_name[48], dst_name[48]; - u_int8_t ip_version; - u_int64_t last_seen; - u_int64_t src2dst_bytes, dst2src_bytes; - u_int32_t src2dst_packets, dst2src_packets; - - // result only, not used for flow identification - ndpi_protocol detected_protocol; - - char info[96]; - char host_server_name[192]; - char bittorent_hash[41]; - - struct { - char client_info[48], server_info[48]; - } ssh_ssl; - - void *src_id, *dst_id; + u_int32_t hashval; + u_int32_t src_ip; + u_int32_t dst_ip; + u_int16_t src_port; + u_int16_t dst_port; + u_int8_t detection_completed, protocol, bidirectional; + u_int16_t vlan_id; + struct ndpi_flow_struct *ndpi_flow; + char src_name[48], dst_name[48]; + u_int8_t ip_version; + u_int64_t last_seen; + u_int64_t src2dst_bytes, dst2src_bytes; + u_int32_t src2dst_packets, dst2src_packets; + + // result only, not used for flow identification + ndpi_protocol detected_protocol; + + char info[96]; + char host_server_name[192]; + char bittorent_hash[41]; + + struct { + char client_info[48], server_info[48]; + } ssh_ssl; + + void *src_id, *dst_id; } ndpi_flow_info_t; - // flow statistics info typedef struct ndpi_stats { - u_int32_t guessed_flow_protocols; - u_int64_t raw_packet_count; - u_int64_t ip_packet_count; - u_int64_t total_wire_bytes, total_ip_bytes, total_discarded_bytes; - u_int64_t protocol_counter[NDPI_MAX_SUPPORTED_PROTOCOLS + NDPI_MAX_NUM_CUSTOM_PROTOCOLS + 1]; - u_int64_t protocol_counter_bytes[NDPI_MAX_SUPPORTED_PROTOCOLS + NDPI_MAX_NUM_CUSTOM_PROTOCOLS + 1]; - u_int32_t protocol_flows[NDPI_MAX_SUPPORTED_PROTOCOLS + NDPI_MAX_NUM_CUSTOM_PROTOCOLS + 1]; - u_int32_t ndpi_flow_count; - u_int64_t tcp_count, udp_count; - u_int64_t mpls_count, pppoe_count, vlan_count, fragmented_count; - u_int64_t packet_len[6]; - u_int16_t max_packet_len; + u_int32_t guessed_flow_protocols; + u_int64_t raw_packet_count; + u_int64_t ip_packet_count; + u_int64_t total_wire_bytes, total_ip_bytes, total_discarded_bytes; + u_int64_t protocol_counter[NDPI_MAX_SUPPORTED_PROTOCOLS + NDPI_MAX_NUM_CUSTOM_PROTOCOLS + 1]; + u_int64_t protocol_counter_bytes[NDPI_MAX_SUPPORTED_PROTOCOLS + NDPI_MAX_NUM_CUSTOM_PROTOCOLS + 1]; + u_int32_t protocol_flows[NDPI_MAX_SUPPORTED_PROTOCOLS + NDPI_MAX_NUM_CUSTOM_PROTOCOLS + 1]; + u_int32_t ndpi_flow_count; + u_int64_t tcp_count, udp_count; + u_int64_t mpls_count, pppoe_count, vlan_count, fragmented_count; + u_int64_t packet_len[6]; + u_int16_t max_packet_len; } ndpi_stats_t; - // flow preferences typedef struct ndpi_workflow_prefs { - u_int8_t decode_tunnels; - u_int8_t quiet_mode; - u_int32_t num_roots; - u_int32_t max_ndpi_flows; + u_int8_t decode_tunnels; + u_int8_t quiet_mode; + u_int32_t num_roots; + u_int32_t max_ndpi_flows; } ndpi_workflow_prefs_t; struct ndpi_workflow; /** workflow, flow, user data */ -typedef void (*ndpi_workflow_callback_ptr) (struct ndpi_workflow *, struct ndpi_flow_info *, void *); - +typedef void (*ndpi_workflow_callback_ptr)(struct ndpi_workflow *, struct ndpi_flow_info *, void *); // workflow main structure typedef struct ndpi_workflow { - u_int64_t last_time; + u_int64_t last_time; - struct ndpi_workflow_prefs prefs; - struct ndpi_stats stats; + struct ndpi_workflow_prefs prefs; + struct ndpi_stats stats; - ndpi_workflow_callback_ptr __flow_detected_callback; - void * __flow_detected_udata; - ndpi_workflow_callback_ptr __flow_giveup_callback; - void * __flow_giveup_udata; + ndpi_workflow_callback_ptr __flow_detected_callback; + void *__flow_detected_udata; + ndpi_workflow_callback_ptr __flow_giveup_callback; + void *__flow_giveup_udata; - /* outside referencies */ - pcap_t *pcap_handle; + /* outside referencies */ + pcap_t *pcap_handle; - /* allocated by prefs */ - void **ndpi_flows_root; - struct ndpi_detection_module_struct *ndpi_struct; + /* allocated by prefs */ + void **ndpi_flows_root; + struct ndpi_detection_module_struct *ndpi_struct; } ndpi_workflow_t; - /* TODO: remove wrappers parameters and use ndpi global, when their initialization will be fixed... */ -struct ndpi_workflow * ndpi_workflow_init(const struct ndpi_workflow_prefs * prefs, pcap_t * pcap_handle); - - - /* workflow main free function */ -void ndpi_workflow_free(struct ndpi_workflow * workflow); +struct ndpi_workflow * +ndpi_workflow_init(const struct ndpi_workflow_prefs *prefs, pcap_t *pcap_handle); +/* workflow main free function */ +void +ndpi_workflow_free(struct ndpi_workflow *workflow); /** Free flow_info ndpi support structures but not the flow_info itself * * TODO remove! Half freeing things is bad! */ -void ndpi_free_flow_info_half(struct ndpi_flow_info *flow); - +void +ndpi_free_flow_info_half(struct ndpi_flow_info *flow); /* Process a packet and update the workflow */ -struct ndpi_proto ndpi_workflow_process_packet(struct ndpi_workflow * workflow, - const struct pcap_pkthdr *header, - const u_char *packet); - +struct ndpi_proto +ndpi_workflow_process_packet(struct ndpi_workflow *workflow, const struct pcap_pkthdr *header, const u_char *packet); /* flow callbacks for complete detected flow (ndpi_flow_info will be freed right after) */ -static inline void ndpi_workflow_set_flow_detected_callback(struct ndpi_workflow * workflow, ndpi_workflow_callback_ptr callback, void * udata) { - workflow->__flow_detected_callback = callback; - workflow->__flow_detected_udata = udata; +static inline void +ndpi_workflow_set_flow_detected_callback(struct ndpi_workflow *workflow, ndpi_workflow_callback_ptr callback, + void *udata) { + workflow->__flow_detected_callback = callback; + workflow->__flow_detected_udata = udata; } /* flow callbacks for sufficient detected flow (ndpi_flow_info will be freed right after) */ -static inline void ndpi_workflow_set_flow_giveup_callback(struct ndpi_workflow * workflow, ndpi_workflow_callback_ptr callback, void * udata) { - workflow->__flow_giveup_callback = callback; - workflow->__flow_giveup_udata = udata; +static inline void +ndpi_workflow_set_flow_giveup_callback(struct ndpi_workflow *workflow, ndpi_workflow_callback_ptr callback, + void *udata) { + workflow->__flow_giveup_callback = callback; + workflow->__flow_giveup_udata = udata; } - /* compare two nodes in workflow */ -int ndpi_workflow_node_cmp(const void *a, const void *b); -void process_ndpi_collected_info(struct ndpi_workflow * workflow, struct ndpi_flow_info *flow); -u_int32_t ethernet_crc32(const void* data, size_t n_bytes); -void ndpi_flow_info_freer(void *node); +/* compare two nodes in workflow */ +int +ndpi_workflow_node_cmp(const void *a, const void *b); +void +process_ndpi_collected_info(struct ndpi_workflow *workflow, struct ndpi_flow_info *flow); +u_int32_t +ethernet_crc32(const void *data, size_t n_bytes); +void +ndpi_flow_info_freer(void *node); #endif diff --git a/examples/nf_router/nf_router.c b/examples/nf_router/nf_router.c index 51a40d8f9..d2fb1c5f9 100644 --- a/examples/nf_router/nf_router.c +++ b/examples/nf_router/nf_router.c @@ -5,8 +5,8 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,22 +38,22 @@ * nf_router.c - route packets based on the provided config. ********************************************************************/ -#include -#include -#include +#include +#include #include #include -#include -#include +#include +#include #include -#include #include +#include +#include +#include #include -#include #include -#include #include +#include #include "onvm_nflib.h" #include "onvm_pkt_helper.h" @@ -62,7 +62,7 @@ /* router information */ uint8_t nf_count; -char * cfg_filename; +char *cfg_filename; struct forward_nf *fwd_nf; struct forward_nf { @@ -70,13 +70,9 @@ struct forward_nf { uint8_t dest; }; -/* Struct that contains information about this NF */ -struct onvm_nf_info *nf_info; - /* number of package between each print */ static uint32_t print_delay = 1000000; - /* * Print a usage message */ @@ -99,26 +95,26 @@ parse_app_args(int argc, char *argv[], const char *progname) { while ((c = getopt(argc, argv, "f:p:")) != -1) { switch (c) { - case 'f': - cfg_filename = strdup(optarg); - break; - case 'p': - print_delay = strtoul(optarg, NULL, 10); - break; - case '?': - usage(progname); - if (optopt == 'd') - RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); - else if (optopt == 'p') - RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); - else if (isprint(optopt)) - RTE_LOG(INFO, APP, "Unknown option `-%c'.\n", optopt); - else - RTE_LOG(INFO, APP, "Unknown option character `\\x%x'.\n", optopt); - return -1; - default: - usage(progname); - return -1; + case 'f': + cfg_filename = strdup(optarg); + break; + case 'p': + print_delay = strtoul(optarg, NULL, 10); + break; + case '?': + usage(progname); + if (optopt == 'd') + RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); + else if (optopt == 'p') + RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); + else if (isprint(optopt)) + RTE_LOG(INFO, APP, "Unknown option `-%c'.\n", optopt); + else + RTE_LOG(INFO, APP, "Unknown option character `\\x%x'.\n", optopt); + return -1; + default: + usage(progname); + return -1; } } @@ -126,17 +122,17 @@ parse_app_args(int argc, char *argv[], const char *progname) { } /* - * This function parses the forward config. It takes the filename - * and fills up the forward nf array. This includes the ip and dest + * This function parses the forward config. It takes the filename + * and fills up the forward nf array. This includes the ip and dest * address of the onvm_nf */ static int parse_router_config(void) { int ret, temp, i; char ip[32]; - FILE * cfg; + FILE *cfg; - cfg = fopen(cfg_filename, "r"); + cfg = fopen(cfg_filename, "r"); if (cfg == NULL) { rte_exit(EXIT_FAILURE, "Error openning server \'%s\' config\n", cfg_filename); } @@ -169,17 +165,16 @@ parse_router_config(void) { } fclose(cfg); - printf("\nDest config (%d):\n",nf_count); + printf("\nDest config (%d):\n", nf_count); for (i = 0; i < nf_count; i++) { - printf("%" PRIu8 ".%" PRIu8 ".%" PRIu8 ".%" PRIu8 " ", - fwd_nf[i].ip & 0xFF, (fwd_nf[i].ip >> 8) & 0xFF, (fwd_nf[i].ip >> 16) & 0xFF, (fwd_nf[i].ip >> 24) & 0xFF); + printf("%" PRIu8 ".%" PRIu8 ".%" PRIu8 ".%" PRIu8 " ", (fwd_nf[i].ip >> 24) & 0xFF, (fwd_nf[i].ip >> 16) & 0xFF, + (fwd_nf[i].ip >> 8) & 0xFF, fwd_nf[i].ip & 0xFF); printf(" %d\n", fwd_nf[i].dest); } return ret; } - /* * This function displays stats. It uses ANSI terminal codes to clear * screen when called. It is called from a single non-master @@ -187,11 +182,11 @@ parse_router_config(void) { * than one lcore enabled. */ static void -do_stats_display(struct rte_mbuf* pkt) { - const char clr[] = { 27, '[', '2', 'J', '\0' }; - const char topLeft[] = { 27, '[', '1', ';', '1', 'H', '\0' }; +do_stats_display(struct rte_mbuf *pkt) { + const char clr[] = {27, '[', '2', 'J', '\0'}; + const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0'}; static uint64_t pkt_process = 0; - struct ipv4_hdr* ip; + struct ipv4_hdr *ip; pkt_process += print_delay; @@ -202,7 +197,7 @@ do_stats_display(struct rte_mbuf* pkt) { printf("-----\n"); printf("Port : %d\n", pkt->port); printf("Size : %d\n", pkt->pkt_len); - printf("N° : %"PRIu64"\n", pkt_process); + printf("N° : %" PRIu64 "\n", pkt_process); printf("\n\n"); ip = onvm_pkt_ipv4_hdr(pkt); @@ -214,11 +209,12 @@ do_stats_display(struct rte_mbuf* pkt) { } static int -packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, __attribute__((unused)) struct onvm_nf_info *nf_info) { +packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, + __attribute__((unused)) struct onvm_nf_local_ctx *nf_local_ctx) { static uint32_t counter = 0; struct ether_hdr *eth_hdr; struct arp_hdr *in_arp_hdr; - struct ipv4_hdr* ip; + struct ipv4_hdr *ip; int i; ip = onvm_pkt_ipv4_hdr(pkt); @@ -229,7 +225,7 @@ packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, __attribute__(( if (rte_cpu_to_be_16(eth_hdr->ether_type) == ETHER_TYPE_ARP) { in_arp_hdr = rte_pktmbuf_mtod_offset(pkt, struct arp_hdr *, sizeof(struct ether_hdr)); for (i = 0; i < nf_count; i++) { - if (in_arp_hdr->arp_data.arp_tip == fwd_nf[i].ip) { + if (rte_be_to_cpu_32(in_arp_hdr->arp_data.arp_tip) == fwd_nf[i].ip) { meta->destination = fwd_nf[i].dest; meta->action = ONVM_NF_ACTION_TONF; return 0; @@ -247,7 +243,7 @@ packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, __attribute__(( } for (i = 0; i < nf_count; i++) { - if (fwd_nf[i].ip == ip->dst_addr) { + if (fwd_nf[i].ip == rte_be_to_cpu_32(ip->dst_addr)) { meta->destination = fwd_nf[i].dest; meta->action = ONVM_NF_ACTION_TONF; return 0; @@ -260,24 +256,41 @@ packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, __attribute__(( return 0; } - -int main(int argc, char *argv[]) { +int +main(int argc, char *argv[]) { int arg_offset; - + struct onvm_nf_local_ctx *nf_local_ctx; + struct onvm_nf_function_table *nf_function_table; const char *progname = argv[0]; - if ((arg_offset = onvm_nflib_init(argc, argv, NF_TAG, &nf_info)) < 0) - return -1; + nf_local_ctx = onvm_nflib_init_nf_local_ctx(); + onvm_nflib_start_signal_handler(nf_local_ctx, NULL); + + nf_function_table = onvm_nflib_init_nf_function_table(); + nf_function_table->pkt_handler = &packet_handler; + + if ((arg_offset = onvm_nflib_init(argc, argv, NF_TAG, nf_local_ctx, nf_function_table)) < 0) { + onvm_nflib_stop(nf_local_ctx); + if (arg_offset == ONVM_SIGNAL_TERMINATION) { + printf("Exiting due to user termination\n"); + return 0; + } else { + rte_exit(EXIT_FAILURE, "Failed ONVM init\n"); + } + } + argc -= arg_offset; argv += arg_offset; if (parse_app_args(argc, argv, progname) < 0) { - onvm_nflib_stop(nf_info); + onvm_nflib_stop(nf_local_ctx); rte_exit(EXIT_FAILURE, "Invalid command-line arguments\n"); } parse_router_config(); - onvm_nflib_run(nf_info, &packet_handler); + onvm_nflib_run(nf_local_ctx); + + onvm_nflib_stop(nf_local_ctx); printf("If we reach here, program is ending\n"); return 0; } diff --git a/examples/payload_scan/.gitignore b/examples/payload_scan/.gitignore new file mode 100644 index 000000000..d2e04ee4f --- /dev/null +++ b/examples/payload_scan/.gitignore @@ -0,0 +1,2 @@ +payload_scan/ +build/ diff --git a/examples/payload_scan/Makefile b/examples/payload_scan/Makefile new file mode 100644 index 000000000..76abccbee --- /dev/null +++ b/examples/payload_scan/Makefile @@ -0,0 +1,62 @@ +# openNetVM +# https://github.com/sdnfv/openNetVM +# +# BSD LICENSE +# +# Copyright(c) +# 2015-2017 George Washington University +# 2015-2017 University of California Riverside +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# The name of the author may not be used to endorse or promote +# products derived from this software without specific prior +# written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +ifeq ($(RTE_SDK),) +$(error "Please define RTE_SDK environment variable") +endif + +RTE_TARGET ?= $(RTE_TARGET) + +# Default target, can be overriden by command line or environment +include $(RTE_SDK)/mk/rte.vars.mk + +# binary name +APP = payload_scan + +# all source are stored in SRCS-y +SRCS-y := payload_scan.c + +# OpenNetVM path +ONVM= $(SRCDIR)/../../onvm + +CFLAGS += $(WERROR_FLAGS) -O3 -g + +CFLAGS += -I$(ONVM)/onvm_nflib +CFLAGS += -I$(ONVM)/lib +LDFLAGS += $(ONVM)/onvm_nflib/$(RTE_TARGET)/libonvm.a +LDFLAGS += $(ONVM)/lib/$(RTE_TARGET)/lib/libonvmhelper.a -lm + +include $(RTE_SDK)/mk/rte.extapp.mk diff --git a/examples/payload_scan/README.md b/examples/payload_scan/README.md new file mode 100644 index 000000000..fda36c204 --- /dev/null +++ b/examples/payload_scan/README.md @@ -0,0 +1,23 @@ +Payload scan +== +The payload scan NF drops/forwards packets based on whether or not a user-input string appears in the packets payload. + +Compilation and Execution +-- +``` +cd examples +make +cd payload_scan +./go.sh SERVICE_ID -d DESTINATION_ID -s INPUT_STRING + +OR + +./go.sh -F CONFIG_FILE -- -- -d DST -s INPUT_STRING [-p PRINT_DELAY] [-i inverse mode] +``` + +App Specific Arguments +-- + - `-i`: If the user-input string appears in the packets payload, drop the packet instead of forwarding it. + - `-s `: String used to search within a packets payload. + - `-p +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "onvm_nflib.h" +#include "onvm_pkt_helper.h" + +#define NF_TAG "payload_scan" + +/* Number of packets between prints */ +static uint32_t print_delay = 10000000; + +/* Destination NF ID */ +static uint16_t destination; + +/* Inverse argument, set to 1 when packets are forwarded on a packet mismatch. */ +static uint8_t forward_on_match = 0; + +/* String to search within packet payload*/ +static char *search_term = NULL; + +struct onvm_pkt_stats { + uint64_t pkt_drop; + uint64_t pkt_accept; + uint64_t pkt_total; + uint64_t pkt_not_ipv4; + uint64_t pkt_not_tcp_udp; +}; + +/* + * Print a usage message + */ +static void +usage(const char *progname) { + printf("Usage:\n"); + printf("%s [EAL args] -- [NF_LIB args] -- -p \n", progname); + printf("%s -F [EAL args] -- [NF_LIB args] -- [NF args]\n\n", progname); + printf("Flags:\n"); + printf(" - `-p `: number of packets between each print, e.g. `-p 1` prints every packets.\n"); + printf(" - `-i `: payload match to search term results in a packet drop, mismatch results in a forward\n"); + printf(" - `-s `: String to match against packet payload\n"); +} + +/* + * Parse the application arguments. + */ +static int +parse_app_args(int argc, char *argv[], const char *progname) { + int c, dst_flag = 0, input_flag = 0; + + while ((c = getopt(argc, argv, "d:s:p:i")) != -1) { + switch (c) { + case 'd': + destination = strtoul(optarg, NULL, 10); + dst_flag = 1; + break; + case 's': + search_term = malloc(sizeof(char) * (strlen(optarg))); + strcpy(search_term, optarg); + RTE_LOG(INFO, APP, "Search term = %s\n", search_term); + input_flag = 1; + break; + case 'p': + print_delay = strtoul(optarg, NULL, 10); + RTE_LOG(INFO, APP, "Print delay = %d\n", print_delay); + break; + + case 'i': + forward_on_match = 1; + RTE_LOG(INFO, APP, + "Inverse mode enabled: packets dropped on string hit and forwarded on mismatch\n"); + break; + case '?': + usage(progname); + if (optopt == 'p') + RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); + else if (isprint(optopt)) + RTE_LOG(INFO, APP, "Unknown option `-%c'.\n", optopt); + else + RTE_LOG(INFO, APP, "Unknown option character `\\x%x'.\n", optopt); + return -1; + default: + usage(progname); + return -1; + } + } + + if (!dst_flag) { + RTE_LOG(INFO, APP, "Payload NF requires a destination NF with the -d flag.\n"); + return -1; + } + if (!input_flag) { + RTE_LOG(INFO, APP, "Payload NF requires a search term string with the -s flag.\n"); + return -1; + } + return optind; +} + +/* + * This function displays stats. It uses ANSI terminal codes to clear + * screen when called. It is called from a single non-master + * thread in the server process, when the process is run with more + * than one lcore enabled. + */ +static void +do_stats_display(struct onvm_nf *nf) { + const char clr[] = {27, '[', '2', 'J', '\0'}; + const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0'}; + struct onvm_pkt_stats *stats = (struct onvm_pkt_stats *) nf->data; + + /* Clear screen and move to top left */ + printf("%s%s", clr, topLeft); + printf("Search term: %s\n", search_term); + printf("Packets accepted: %lu\n", stats->pkt_accept); + printf("Packets dropped: %lu\n", stats->pkt_drop); + printf("Packets not IPv4: %lu\n", stats->pkt_not_ipv4); + printf("Packets not UDP/TCP: %lu\n", stats->pkt_not_tcp_udp); + printf("Packets total: %lu", stats->pkt_total); + + printf("\n\n"); +} + +static int packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, struct onvm_nf_local_ctx *nf_local_ctx) { + int udp_pkt, tcp_pkt; + char search_match; + static uint32_t counter = 0; + uint8_t *pkt_data; + struct onvm_pkt_stats *stats = (struct onvm_pkt_stats *) nf_local_ctx->nf->data; + + if (++counter == print_delay) { + do_stats_display(nf_local_ctx->nf); + counter = 0; + } + + stats->pkt_total++; + + if (!onvm_pkt_is_ipv4(pkt)) { + meta->action = ONVM_NF_ACTION_DROP; + stats->pkt_drop++; + return 0; + } + + udp_pkt = onvm_pkt_is_udp(pkt); + tcp_pkt = onvm_pkt_is_tcp(pkt); + pkt_data = NULL; + + if (!udp_pkt && !tcp_pkt) { + meta->action = ONVM_NF_ACTION_DROP; + stats->pkt_not_tcp_udp++; + return 0; + } + + if (udp_pkt) { + pkt_data = rte_pktmbuf_mtod_offset(pkt, uint8_t * , sizeof(struct ether_hdr) + sizeof(struct ipv4_hdr) + + sizeof(struct udp_hdr)); + } else { + pkt_data = rte_pktmbuf_mtod_offset(pkt, uint8_t * , sizeof(struct ether_hdr) + sizeof(struct ipv4_hdr) + + sizeof(struct tcp_hdr)); + } + + search_match = strstr((const char *) pkt_data, search_term) != NULL; + + if ((search_match && !forward_on_match) || (!search_match && forward_on_match)) { + meta->action = ONVM_NF_ACTION_TONF; + meta->destination = destination; + stats->pkt_accept++; + } else { + meta->action = ONVM_NF_ACTION_DROP; + stats->pkt_drop++; + } + + return 0; +} + + +int main(int argc, char *argv[]) { + int arg_offset; + struct onvm_pkt_stats *stats; + struct onvm_nf_local_ctx *nf_local_ctx; + struct onvm_nf_function_table *nf_function_table; + const char *progname = argv[0]; + + nf_local_ctx = onvm_nflib_init_nf_local_ctx(); + onvm_nflib_start_signal_handler(nf_local_ctx, NULL); + + nf_function_table = onvm_nflib_init_nf_function_table(); + nf_function_table->pkt_handler = &packet_handler; + + if ((arg_offset = onvm_nflib_init(argc, argv, NF_TAG, nf_local_ctx, nf_function_table)) < 0) { + onvm_nflib_stop(nf_local_ctx); + if (arg_offset == ONVM_SIGNAL_TERMINATION) { + printf("Exiting due to user termination\n"); + return 0; + } else { + rte_exit(EXIT_FAILURE, "Failed ONVM init\n"); + } + } + + argc -= arg_offset; + argv += arg_offset; + + stats = (struct onvm_pkt_stats *) rte_zmalloc("stats", sizeof(struct onvm_pkt_stats), 0); + nf_local_ctx->nf->data = (void *) stats; + + if (parse_app_args(argc, argv, progname) < 0) { + onvm_nflib_stop(nf_local_ctx); + rte_exit(EXIT_FAILURE, "Invalid command-line arguments\n"); + } + + onvm_nflib_run(nf_local_ctx); + + onvm_nflib_stop(nf_local_ctx); + printf("If we reach here, program is ending\n"); + return 0; +} diff --git a/examples/scaling_example/README.md b/examples/scaling_example/README.md index 3e6ec903c..40d0480c3 100644 --- a/examples/scaling_example/README.md +++ b/examples/scaling_example/README.md @@ -1,11 +1,12 @@ Scaling Example NF == -This program showcases how to use NF scaling. It shows how to use the scaling api of onvm and showcases a few simple examples. +This program showcases how to use NF scaling. It shows how to use the ONVM scaling API or use the advanced rings mode and implement your own scaling logic. +The NF will spawn X children (provided by `-n`, defaulting to 1 if arg isn't provided) and send packets to destination NF. -Has 2 modes +Has 2 modes: - - First by providing the service and dest without the advanced rings mode, the NF will create a child with the service id of the destination. That child will then create additional children up until it reaches the set children count. - - Second with the advanced rings flag, the NF will create as many children as set, with the service id provided and forward packets to set dest. + - By default using the nflib ring processing, the NF will use the functions provided in the nf_function_table callbacks. + - Second with the advanced rings flag, the NF will do its own pthread creation, manage the NF rx/tx ring on its own.. Compilation and Execution -- @@ -17,11 +18,12 @@ cd scaling_example OR -sudo ./build/app/scaling -l CORELIST -n 3 --proc-type=secondary -- -r SERVICE_ID -- -d DST [-a] +sudo ./build/app/scaling -l CORELIST -n 3 --proc-type=secondary -- -r SERVICE_ID -- -d DST [-n] [-a] ``` App Specific Arguments -- - `-d DST`: Destination Service ID, functionality depends on mode - `-n NUM_CHILDREN`: Sets the number of children for the NF to spawn + - `-c`: Set spawned children to use shared cpu core allocation - `-a`: Use advanced rings interface instead of default `packet_handler` diff --git a/examples/scaling_example/scaling.c b/examples/scaling_example/scaling.c index be45b65f0..45a296a7a 100644 --- a/examples/scaling_example/scaling.c +++ b/examples/scaling_example/scaling.c @@ -5,8 +5,8 @@ * BSD LICENSE * * Copyright(c) - * 2015-2018 George Washington University - * 2015-2018 University of California Riverside + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,36 +38,37 @@ * scaling.c - Example usage of the provided scaling functionality. ********************************************************************/ -#include -#include -#include +#include +#include #include +#include #include -#include -#include +#include +#include #include -#include #include -#include +#include +#include #include -#include -#include -#include #include -#include #include #include +#include #include +#include +#include +#include +#include +#include "onvm_flow_table.h" #include "onvm_nflib.h" #include "onvm_pkt_helper.h" -#include "onvm_flow_table.h" #define NF_TAG "scaling" #define PKTMBUF_POOL_NAME "MProc_pktmbuf_pool" -#define PKT_READ_SIZE ((uint16_t)32) +#define PKT_READ_SIZE ((uint16_t)32) #define LOCAL_EXPERIMENTAL_ETHER 0x88B5 #define DEFAULT_PKT_NUM 128 #define MAX_PKT_NUM NF_QUEUE_RINGSIZE @@ -75,14 +76,28 @@ static uint16_t destination; static uint16_t num_children = DEFAULT_NUM_CHILDREN; -static uint8_t use_direct_rings = 0; -static uint8_t keep_running = 1; +static uint8_t use_advanced_rings = 0; +static uint8_t use_shared_core_allocation = 0; static uint8_t d_addr_bytes[ETHER_ADDR_LEN]; static uint16_t packet_size = ETHER_HDR_LEN; static uint32_t packet_number = DEFAULT_PKT_NUM; -void nf_setup(struct onvm_nf_info *nf_info); +/* For advanced rings scaling */ +rte_atomic16_t signal_exit_flag; +uint8_t ONVM_NF_SHARE_CORES; +struct child_spawn_info { + struct onvm_nf_init_cfg *child_cfg; + struct onvm_nf *parent; +}; + +void nf_setup(struct onvm_nf_local_ctx *nf_local_ctx); +void sig_handler(int sig); +void *start_child(void *arg); +int thread_main_loop(struct onvm_nf_local_ctx *nf_local_ctx); + +static void run_advanced_rings(int argc, char *argv[]); +static void run_default_nflib_mode(int argc, char *argv[]); /* * Print a usage message @@ -95,6 +110,7 @@ usage(const char *progname) { printf("Flags:\n"); printf(" - `-d DST`: Destination Service ID, functionality depends on mode\n"); printf(" - `-n NUM_CHILDREN`: Sets the number of children for the NF to spawn\n"); + printf(" - `-c`: Set NF core allocation to shared core\n"); printf(" - `-a`: Use advanced rings interface instead of default `packet_handler`\n"); } @@ -105,32 +121,31 @@ static int parse_app_args(int argc, char *argv[], const char *progname) { int c, dst_flag = 0; - while ((c = getopt(argc, argv, "d:n:p:a")) != -1) { + while ((c = getopt(argc, argv, "d:n:p:ac")) != -1) { switch (c) { - case 'a': - use_direct_rings = 1; - break; - case 'd': - destination = strtoul(optarg, NULL, 10); - dst_flag = 1; - break; - case 'n': - num_children = strtoul(optarg, NULL, 10); - if (num_children < DEFAULT_NUM_CHILDREN) { - printf("The number of children should be more or equal than %d\n", DEFAULT_NUM_CHILDREN); + case 'c': + use_shared_core_allocation = 1; + break; + case 'a': + use_advanced_rings = 1; + break; + case 'd': + destination = strtoul(optarg, NULL, 10); + dst_flag = 1; + break; + case 'n': + num_children = strtoul(optarg, NULL, 10); + break; + case '?': + usage(progname); + if (isprint(optopt)) + RTE_LOG(INFO, APP, "Unknown option `-%c'.\n", optopt); + else + RTE_LOG(INFO, APP, "Unknown option character `\\x%x'.\n", optopt); + return -1; + default: + usage(progname); return -1; - } - break; - case '?': - usage(progname); - if (isprint(optopt)) - RTE_LOG(INFO, APP, "Unknown option `-%c'.\n", optopt); - else - RTE_LOG(INFO, APP, "Unknown option character `\\x%x'.\n", optopt); - return -1; - default: - usage(progname); - return -1; } } @@ -143,157 +158,217 @@ parse_app_args(int argc, char *argv[], const char *progname) { } /* - * Basic packet handler, just forwards all packets + * Generates fake packets and enqueues them into the tx ring + */ +void +nf_setup(__attribute__((unused)) struct onvm_nf_local_ctx *nf_local_ctx) { + uint32_t i; + struct rte_mempool *pktmbuf_pool; + + pktmbuf_pool = rte_mempool_lookup(PKTMBUF_POOL_NAME); + if (pktmbuf_pool == NULL) { + onvm_nflib_stop(nf_local_ctx); + rte_exit(EXIT_FAILURE, "Cannot find mbuf pool!\n"); + } + + for (i = 0; i < packet_number; ++i) { + struct onvm_pkt_meta *pmeta; + struct ether_hdr *ehdr; + int j; + + struct rte_mbuf *pkt = rte_pktmbuf_alloc(pktmbuf_pool); + if (pkt == NULL) + break; + + /* set up ether header and set new packet size */ + ehdr = (struct ether_hdr *)rte_pktmbuf_append(pkt, packet_size); + + /* Using manager mac addr for source*/ + rte_eth_macaddr_get(0, &ehdr->s_addr); + for (j = 0; j < ETHER_ADDR_LEN; ++j) { + ehdr->d_addr.addr_bytes[j] = d_addr_bytes[j]; + } + ehdr->ether_type = LOCAL_EXPERIMENTAL_ETHER; + + pmeta = onvm_get_pkt_meta(pkt); + pmeta->destination = destination; + pmeta->action = ONVM_NF_ACTION_TONF; + pkt->hash.rss = i; + pkt->port = 0; + + onvm_nflib_return_pkt(nf_local_ctx->nf, pkt); + } +} + + +/* + * Basic packet handler, just forwards all packets to destination */ static int -packet_handler_fwd(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, __attribute__((unused)) struct onvm_nf_info *nf_info) { +packet_handler_fwd(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, + __attribute__((unused)) struct onvm_nf_local_ctx *nf_local_ctx) { (void)pkt; - meta->destination = *(uint16_t *)nf_info->data; + meta->destination = destination; meta->action = ONVM_NF_ACTION_TONF; return 0; } /* - * Child packet handler showcasing that children can also spawn NFs on their own + * Main packet handler for default rings main NF */ static int -packet_handler_child(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, __attribute__((unused)) struct onvm_nf_info *nf_info) { +packet_handler_with_scaling(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, + __attribute__((unused)) struct onvm_nf_local_ctx *nf_local_ctx) { (void)pkt; - /* As this is already a child, 1 NF has been spawned */ - static int spawned_nfs = 1; - meta->destination = *(uint16_t *)nf_info->data; - meta->action = ONVM_NF_ACTION_TONF; + static uint32_t spawned_nfs = 0; /* Spawn children until we hit the set number */ while (spawned_nfs < num_children) { - struct onvm_nf_scale_info *scale_info = onvm_nflib_get_empty_scaling_config(nf_info); - uint16_t *state_data = rte_malloc("nf_state_data", sizeof(uint16_t), 0); - *state_data = nf_info->service_id; + struct onvm_nf_scale_info *scale_info = onvm_nflib_get_empty_scaling_config(nf_local_ctx->nf); /* Sets service id of child */ - scale_info->service_id = destination; + scale_info->nf_init_cfg->service_id = destination; + scale_info->function_table = onvm_nflib_init_nf_function_table(); /* Run the setup function to generate packets */ - scale_info->setup_func = &nf_setup; + scale_info->function_table->setup = &nf_setup; /* Custom packet handler */ - scale_info->pkt_func = &packet_handler_fwd; - /* Insert state data, will be used to forward packets to itself */ - scale_info->data = state_data; + scale_info->function_table->pkt_handler = &packet_handler_fwd; + if (use_shared_core_allocation) + scale_info->nf_init_cfg->init_options = ONVM_SET_BIT(0, SHARE_CORE_BIT); /* Spawn the child */ if (onvm_nflib_scale(scale_info) == 0) - RTE_LOG(INFO, APP, "Spawning child SID %u; with packet_handler_fwd packet function\n", scale_info->service_id); + RTE_LOG(INFO, APP, "Spawning child SID %u; with packet_handler_fwd packet function\n", + scale_info->nf_init_cfg->service_id); else rte_exit(EXIT_FAILURE, "Can't spawn child\n"); spawned_nfs++; } + meta->destination = destination; + meta->action = ONVM_NF_ACTION_TONF; + return 0; } -/* - * Main packet handler - */ -static int -packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, __attribute__((unused)) struct onvm_nf_info *nf_info) { - (void)pkt; - static uint32_t spawned_child = 0; - struct onvm_nf_scale_info *scale_info; - void *data; - - /* Testing NF scaling, Spawns one child */ - if (spawned_child == 0) { - spawned_child = 1; - - /* Prepare state data for the child */ - data = (void *)rte_malloc("nf_state_data", sizeof(uint16_t), 0); - *(uint16_t *)data = destination; - /* Get the filled in scale struct by inheriting parent properties */ - scale_info = onvm_nflib_inherit_parent_config(nf_info, data); - scale_info->service_id = destination; - scale_info->pkt_func = &packet_handler_child; +static void +run_default_nflib_mode(int argc, char *argv[]) { + struct onvm_nf_local_ctx *nf_local_ctx; + struct onvm_nf_function_table *nf_function_table; + const char *progname = argv[0]; + int arg_offset; - /* Spawn the child */ - if (onvm_nflib_scale(scale_info) == 0) - RTE_LOG(INFO, APP, "Spawning child SID %u; with packet_handler_child packet function\n", scale_info->service_id); - else - rte_exit(EXIT_FAILURE, "Can't initialize the first child!\n"); + nf_local_ctx = onvm_nflib_init_nf_local_ctx(); + onvm_nflib_start_signal_handler(nf_local_ctx, NULL); + nf_function_table = onvm_nflib_init_nf_function_table(); + nf_function_table->pkt_handler = &packet_handler_with_scaling; + nf_function_table->setup = &nf_setup; + + if ((arg_offset = onvm_nflib_init(argc, argv, NF_TAG, nf_local_ctx, nf_function_table)) < 0) { + onvm_nflib_stop(nf_local_ctx); + if (arg_offset == ONVM_SIGNAL_TERMINATION) { + printf("Exiting due to user termination\n"); + return; + } else { + rte_exit(EXIT_FAILURE, "Failed ONVM init\n"); + } } - meta->destination = nf_info->service_id; - meta->action = ONVM_NF_ACTION_TONF; + argc -= arg_offset; + argv += arg_offset; - return 0; + if (parse_app_args(argc, argv, progname) < 0) { + onvm_nflib_stop(nf_local_ctx); + rte_exit(EXIT_FAILURE, "Invalid command-line arguments\n"); + } + + onvm_nflib_run(nf_local_ctx); + onvm_nflib_stop(nf_local_ctx); } +void * +start_child(void *arg) { + struct onvm_nf_local_ctx *child_local_ctx; + struct onvm_nf_init_cfg *child_init_cfg; + struct onvm_nf *parent; + struct child_spawn_info *spawn_info; + + spawn_info = (struct child_spawn_info *)arg; + child_init_cfg = spawn_info->child_cfg; + parent = spawn_info->parent; + child_local_ctx = onvm_nflib_init_nf_local_ctx(); + + if (onvm_nflib_start_nf(child_local_ctx, child_init_cfg) < 0) { + printf("Failed to spawn child NF\n"); + return NULL; + } -static void -handle_signal(int sig) { - if (sig == SIGINT || sig == SIGTERM) - keep_running = 0; + /* Keep track of parent for proper termination */ + child_local_ctx->nf->thread_info.parent = parent->instance_id; + + thread_main_loop(child_local_ctx); + onvm_nflib_stop(child_local_ctx); + free(spawn_info); + return NULL; } -static void -run_advanced_rings(struct onvm_nf_info *nf_info) { +int +thread_main_loop(struct onvm_nf_local_ctx *nf_local_ctx) { void *pkts[PKT_READ_SIZE]; - struct onvm_pkt_meta* meta; + struct onvm_pkt_meta *meta; uint16_t i, j, nb_pkts; void *pktsTX[PKT_READ_SIZE]; int tx_batch_size; struct rte_ring *rx_ring; struct rte_ring *tx_ring; - volatile struct onvm_nf *nf; - static uint8_t spawned_nfs = 0; - - /* Listen for ^C and docker stop so we can exit gracefully */ - signal(SIGINT, handle_signal); - signal(SIGTERM, handle_signal); + struct rte_ring *msg_q; + struct onvm_nf *nf; + struct onvm_nf_msg *msg; + struct rte_mempool *nf_msg_pool; - printf("Process %d handling packets using advanced rings\n", nf_info->instance_id); - printf("[Press Ctrl-C to quit ...]\n"); + nf = nf_local_ctx->nf; - /* Set core affinity, as this is adv rings we do it on our own */ - onvm_threading_core_affinitize(nf_info->core); + onvm_nflib_nf_ready(nf); + nf_setup(nf_local_ctx); /* Get rings from nflib */ - nf = onvm_nflib_get_nf(nf_info->instance_id); rx_ring = nf->rx_q; tx_ring = nf->tx_q; - - /* Testing NF scaling */ - if (spawned_nfs == 0) { - /* As this is advanced rings if we want the children to inherit the same function we need to set it first */ - nf->nf_advanced_rings_function = &run_advanced_rings; - struct onvm_nf_scale_info *scale_info; - - /* Spawn children until we hit the set number */ - while (spawned_nfs < num_children) { - /* Prepare state data for the child */ - void *data = (void *)rte_malloc("nf_specific_data", sizeof(uint16_t), 0); - *(uint16_t *)data = destination; - /* Get the filled in scale struct by inheriting parent properties */ - scale_info = onvm_nflib_inherit_parent_config(nf_info, data); - RTE_LOG(INFO, APP, "Tring to spawn child SID %u; running advanced_rings\n", scale_info->service_id); - if (onvm_nflib_scale(scale_info) == 0) - RTE_LOG(INFO, APP, "Spawning child SID %u\n", scale_info->service_id); - else - rte_exit(EXIT_FAILURE, "Can't initialize the child!\n"); - spawned_nfs++; + msg_q = nf->msg_q; + nf_msg_pool = rte_mempool_lookup(_NF_MSG_POOL_NAME); + + printf("Process %d handling packets using advanced rings\n", nf->instance_id); + if (onvm_threading_core_affinitize(nf->thread_info.core) < 0) + rte_exit(EXIT_FAILURE, "Failed to affinitize to core %d\n", nf->thread_info.core); + + while (!rte_atomic16_read(&signal_exit_flag)) { + /* Check for a stop message from the manager */ + if (unlikely(rte_ring_count(msg_q) > 0)) { + msg = NULL; + rte_ring_dequeue(msg_q, (void **)(&msg)); + if (msg->msg_type == MSG_STOP) { + rte_atomic16_set(&signal_exit_flag, 1); + } else { + printf("Received message %d, ignoring", msg->msg_type); + } + rte_mempool_put(nf_msg_pool, (void *)msg); } - } - - while (keep_running && rx_ring && tx_ring && nf) { tx_batch_size = 0; - /* Dequeue all packets in ring up to max possible. */ + /* Dequeue all packets in ring up to max possible */ nb_pkts = rte_ring_dequeue_burst(rx_ring, pkts, PKT_READ_SIZE, NULL); if (unlikely(nb_pkts == 0)) { + if (ONVM_NF_SHARE_CORES) { + rte_atomic16_set(nf->shared_core.sleep_state, 1); + sem_wait(nf->shared_core.nf_mutex); + } continue; } /* Process all the packets */ for (i = 0; i < nb_pkts; i++) { - meta = onvm_get_pkt_meta((struct rte_mbuf*)pkts[i]); - packet_handler_fwd((struct rte_mbuf*)pkts[i], meta, nf_info); + meta = onvm_get_pkt_meta((struct rte_mbuf *)pkts[i]); + packet_handler_fwd((struct rte_mbuf *)pkts[i], meta, nf_local_ctx); pktsTX[tx_batch_size++] = pkts[i]; } @@ -306,90 +381,99 @@ run_advanced_rings(struct onvm_nf_info *nf_info) { nf->stats.tx += tx_batch_size; } } - /* Waiting for spawned NFs to exit */ - for (i = 0; i < MAX_NFS; i++) { - struct onvm_nf *nf_cur = onvm_nflib_get_nf(i); - while(nf_cur && nf_cur->parent == nf->instance_id && nf_cur->info != NULL) { - sleep(1); - } - } - onvm_nflib_stop(nf_info); + return 0; } +void sig_handler(int sig) { + if (sig != SIGINT && sig != SIGTERM) + return; -/* - * Generates fake packets or loads them from a pcap file - */ -void -nf_setup(__attribute__((unused)) struct onvm_nf_info *nf_info) { - uint32_t i; - struct rte_mempool *pktmbuf_pool; - - pktmbuf_pool = rte_mempool_lookup(PKTMBUF_POOL_NAME); - if (pktmbuf_pool == NULL) { - onvm_nflib_stop(nf_info); - rte_exit(EXIT_FAILURE, "Cannot find mbuf pool!\n"); - } - - for (i = 0; i < packet_number; ++i) { - struct onvm_pkt_meta* pmeta; - struct ether_hdr *ehdr; - int j; - - struct rte_mbuf *pkt = rte_pktmbuf_alloc(pktmbuf_pool); - - /* set up ether header and set new packet size */ - ehdr = (struct ether_hdr *) rte_pktmbuf_append(pkt, packet_size); - - /* Using manager mac addr for source*/ - rte_eth_macaddr_get(0, &ehdr->s_addr); - for (j = 0; j < ETHER_ADDR_LEN; ++j) { - ehdr->d_addr.addr_bytes[j] = d_addr_bytes[j]; - } - ehdr->ether_type = LOCAL_EXPERIMENTAL_ETHER; - - pmeta = onvm_get_pkt_meta(pkt); - pmeta->destination = *(uint16_t *)nf_info->data; - pmeta->action = ONVM_NF_ACTION_TONF; - pkt->hash.rss = i; - pkt->port = 0; - - onvm_nflib_return_pkt(nf_info, pkt); - } + /* Will stop the processing for all spawned threads in advanced rings mode */ + rte_atomic16_set(&signal_exit_flag, 1); } -int main(int argc, char *argv[]) { - int arg_offset; +static void +run_advanced_rings(int argc, char *argv[]) { + pthread_t nf_thread[num_children]; + struct onvm_configuration *onvm_config; + struct onvm_nf_local_ctx *nf_local_ctx; + struct onvm_nf_function_table *nf_function_table; + struct onvm_nf *nf; const char *progname = argv[0]; - struct onvm_nf_info *nf_info; - - if ((arg_offset = onvm_nflib_init(argc, argv, NF_TAG, &nf_info)) < 0) - return -1; + int arg_offset, i; + + nf_local_ctx = onvm_nflib_init_nf_local_ctx(); + /* If we're using advanced rings also pass a custom cleanup function, + * this can be used to handle NF specific (non onvm) cleanup logic */ + rte_atomic16_init(&signal_exit_flag); + rte_atomic16_set(&signal_exit_flag, 0); + onvm_nflib_start_signal_handler(nf_local_ctx, sig_handler); + /* No need to define a function table as adv rings won't run onvm_nflib_run */ + nf_function_table = NULL; + + if ((arg_offset = onvm_nflib_init(argc, argv, NF_TAG, nf_local_ctx, nf_function_table)) < 0) { + onvm_nflib_stop(nf_local_ctx); + if (arg_offset == ONVM_SIGNAL_TERMINATION) { + printf("Exiting due to user termination\n"); + return; + } else { + rte_exit(EXIT_FAILURE, "Failed ONVM init\n"); + } + } argc -= arg_offset; argv += arg_offset; if (parse_app_args(argc, argv, progname) < 0) { - onvm_nflib_stop(nf_info); + onvm_nflib_stop(nf_local_ctx); rte_exit(EXIT_FAILURE, "Invalid command-line arguments\n"); } - /* Set the function to execute before running the NF - * For advanced rings manually run the function */ - onvm_nflib_set_setup_function(nf_info, &nf_setup); + nf = nf_local_ctx->nf; + onvm_config = onvm_nflib_get_onvm_config(); + ONVM_NF_SHARE_CORES = onvm_config->flags.ONVM_NF_SHARE_CORES; + + for (i = 0; i < num_children; i++) { + struct onvm_nf_init_cfg *child_cfg; + child_cfg = onvm_nflib_init_nf_init_cfg(nf->tag); + /* Prepare init data for the child */ + child_cfg->service_id = nf->service_id; + struct child_spawn_info *child_data = malloc(sizeof(struct child_spawn_info)); + child_data->child_cfg = child_cfg; + child_data->parent = nf; + /* Increment the children count so that stats are displayed and NF does proper cleanup */ + rte_atomic16_inc(&nf->thread_info.children_cnt); + pthread_create(&nf_thread[i], NULL, start_child, (void *)child_data); + } + + thread_main_loop(nf_local_ctx); + onvm_nflib_stop(nf_local_ctx); + + for (i = 0; i < num_children; i++) { + pthread_join(nf_thread[i], NULL); + } +} - nf_info->data = (void *)rte_malloc("nf_specific_data", sizeof(uint16_t), 0); - *(uint16_t*)nf_info->data = nf_info->service_id; +int +main(int argc, char *argv[]) { + int i; - if (use_direct_rings) { + /* Hack to know if we're using advanced rings before running getopts */ + for (i = argc - 1; i > 0; i--) { + if (strcmp(argv[i], "-a") == 0) + use_advanced_rings = 1; + else if (strcmp(argv[i],"--") == 0) + break; + } + + if (use_advanced_rings) { printf("\nRUNNING ADVANCED RINGS EXPERIMENT\n"); - onvm_nflib_nf_ready(nf_info); - nf_setup(nf_info); - run_advanced_rings(nf_info); + run_advanced_rings(argc, argv); } else { printf("\nRUNNING PACKET_HANDLER EXPERIMENT\n"); - onvm_nflib_run(nf_info, &packet_handler); + run_default_nflib_mode(argc, argv); } + printf("If we reach here, program is ending\n"); return 0; } diff --git a/examples/simple_forward/forward.c b/examples/simple_forward/forward.c index fce24b8b2..2fc321336 100644 --- a/examples/simple_forward/forward.c +++ b/examples/simple_forward/forward.c @@ -5,8 +5,8 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,33 +38,29 @@ * forward.c - an example using onvm. Forwards packets to a DST NF. ********************************************************************/ -#include -#include -#include +#include +#include #include #include -#include -#include +#include +#include #include -#include #include +#include +#include #include -#include #include +#include #include "onvm_nflib.h" #include "onvm_pkt_helper.h" #define NF_TAG "simple_forward" -/* Struct that contains information about this NF */ -struct onvm_nf_info *nf_info; - /* number of package between each print */ static uint32_t print_delay = 1000000; - static uint32_t destination; /* @@ -89,27 +85,27 @@ parse_app_args(int argc, char *argv[], const char *progname) { while ((c = getopt(argc, argv, "d:p:")) != -1) { switch (c) { - case 'd': - destination = strtoul(optarg, NULL, 10); - dst_flag = 1; - break; - case 'p': - print_delay = strtoul(optarg, NULL, 10); - break; - case '?': - usage(progname); - if (optopt == 'd') - RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); - else if (optopt == 'p') - RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); - else if (isprint(optopt)) - RTE_LOG(INFO, APP, "Unknown option `-%c'.\n", optopt); - else - RTE_LOG(INFO, APP, "Unknown option character `\\x%x'.\n", optopt); - return -1; - default: - usage(progname); - return -1; + case 'd': + destination = strtoul(optarg, NULL, 10); + dst_flag = 1; + break; + case 'p': + print_delay = strtoul(optarg, NULL, 10); + break; + case '?': + usage(progname); + if (optopt == 'd') + RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); + else if (optopt == 'p') + RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); + else if (isprint(optopt)) + RTE_LOG(INFO, APP, "Unknown option `-%c'.\n", optopt); + else + RTE_LOG(INFO, APP, "Unknown option character `\\x%x'.\n", optopt); + return -1; + default: + usage(progname); + return -1; } } @@ -128,11 +124,11 @@ parse_app_args(int argc, char *argv[], const char *progname) { * than one lcore enabled. */ static void -do_stats_display(struct rte_mbuf* pkt) { - const char clr[] = { 27, '[', '2', 'J', '\0' }; - const char topLeft[] = { 27, '[', '1', ';', '1', 'H', '\0' }; +do_stats_display(struct rte_mbuf *pkt) { + const char clr[] = {27, '[', '2', 'J', '\0'}; + const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0'}; static uint64_t pkt_process = 0; - struct ipv4_hdr* ip; + struct ipv4_hdr *ip; pkt_process += print_delay; @@ -143,7 +139,7 @@ do_stats_display(struct rte_mbuf* pkt) { printf("-----\n"); printf("Port : %d\n", pkt->port); printf("Size : %d\n", pkt->pkt_len); - printf("N° : %"PRIu64"\n", pkt_process); + printf("N° : %" PRIu64 "\n", pkt_process); printf("\n\n"); ip = onvm_pkt_ipv4_hdr(pkt); @@ -155,7 +151,8 @@ do_stats_display(struct rte_mbuf* pkt) { } static int -packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, __attribute__((unused)) struct onvm_nf_info *nf_info) { +packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, + __attribute__((unused)) struct onvm_nf_local_ctx *nf_local_ctx) { static uint32_t counter = 0; if (++counter == print_delay) { do_stats_display(pkt); @@ -167,23 +164,41 @@ packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, __attribute__(( return 0; } - -int main(int argc, char *argv[]) { +int +main(int argc, char *argv[]) { + struct onvm_nf_local_ctx *nf_local_ctx; + struct onvm_nf_function_table *nf_function_table; int arg_offset; const char *progname = argv[0]; - if ((arg_offset = onvm_nflib_init(argc, argv, NF_TAG, &nf_info)) < 0) - return -1; + nf_local_ctx = onvm_nflib_init_nf_local_ctx(); + onvm_nflib_start_signal_handler(nf_local_ctx, NULL); + + nf_function_table = onvm_nflib_init_nf_function_table(); + nf_function_table->pkt_handler = &packet_handler; + + if ((arg_offset = onvm_nflib_init(argc, argv, NF_TAG, nf_local_ctx, nf_function_table)) < 0) { + onvm_nflib_stop(nf_local_ctx); + if (arg_offset == ONVM_SIGNAL_TERMINATION) { + printf("Exiting due to user termination\n"); + return 0; + } else { + rte_exit(EXIT_FAILURE, "Failed ONVM init\n"); + } + } + argc -= arg_offset; argv += arg_offset; if (parse_app_args(argc, argv, progname) < 0) { - onvm_nflib_stop(nf_info); + onvm_nflib_stop(nf_local_ctx); rte_exit(EXIT_FAILURE, "Invalid command-line arguments\n"); } - onvm_nflib_run(nf_info, &packet_handler); + onvm_nflib_run(nf_local_ctx); + + onvm_nflib_stop(nf_local_ctx); printf("If we reach here, program is ending\n"); return 0; } diff --git a/examples/speed_tester/README.md b/examples/speed_tester/README.md index cf3400d59..9510ed44d 100644 --- a/examples/speed_tester/README.md +++ b/examples/speed_tester/README.md @@ -26,17 +26,16 @@ cd speed_tester OR -./go.sh -F CONFIG_FILE -- -- -d DST_ID [-p PRINT_DELAY] [-a ADVANCED_RINGS] [-s PACKET_SIZE] [-m DEST_MAC] [-o PCAP_FILE] [-l MEASURE_LATENCY] +./go.sh -F CONFIG_FILE -- -- -d DST_ID [-p PRINT_DELAY] [-s PACKET_SIZE] [-m DEST_MAC] [-o PCAP_FILE] [-l MEASURE_LATENCY] OR -sudo ./build/speed_tester -l CORELIST -n 3 --proc-type=secondary -- -r SERVICE_ID -- -d DST [-a] [-p PRINT_DELAY] [-s PACKET_SIZE] [-m DEST_MAC] [-o PCAP_FILENAME] [-l] +sudo ./build/speed_tester -l CORELIST -n 3 --proc-type=secondary -- -r SERVICE_ID -- -d DST [-p PRINT_DELAY] [-s PACKET_SIZE] [-m DEST_MAC] [-o PCAP_FILENAME] [-l] ``` App Specific Arguments -- - `-d DST`: Destination Service ID to foward to - - `-a`: Use advanced rings interface instead of default `packet_handler` - `-p PRINT_DELAY`: Number of packets between each print, e.g. `-p 1` prints every packets. - `-s PACKET_SIZE`: Size of packet, e.g. `-s 32` allocates 32 bytes for the data segment of `rte_mbuf`. - `-m DEST_MAC`: User specified destination MAC address, e.g. `-m aa:bb:cc:dd:ee:ff` sets the destination address within the ethernet header that is located at the start of the packet data. diff --git a/examples/speed_tester/speed_tester.c b/examples/speed_tester/speed_tester.c index 808ebf29e..f8c563cfe 100644 --- a/examples/speed_tester/speed_tester.c +++ b/examples/speed_tester/speed_tester.c @@ -5,8 +5,8 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,39 +38,39 @@ * speed_tester.c - create pkts and loop through NFs. ********************************************************************/ -#include -#include -#include +#include +#include #include +#include #include -#include -#include +#include +#include #include -#include #include -#include +#include +#include #include -#include -#include -#include #include -#include #include #include +#include +#include +#include +#include #ifdef LIBPCAP #include #endif +#include "onvm_flow_table.h" #include "onvm_nflib.h" #include "onvm_pkt_helper.h" -#include "onvm_flow_table.h" -#define NF_TAG "speed" +#define NF_TAG "speed_tester" #define PKTMBUF_POOL_NAME "MProc_pktmbuf_pool" -#define PKT_READ_SIZE ((uint16_t)32) +#define PKT_READ_SIZE ((uint16_t)32) #define SPEED_TESTER_BIT 7 #define LATENCY_BIT 6 #define LOCAL_EXPERIMENTAL_ETHER 0x88B5 @@ -78,14 +78,9 @@ #define DEFAULT_LAT_PKT_NUM 16 #define MAX_PKT_NUM NF_QUEUE_RINGSIZE -/* Struct that contains information about this NF */ -struct onvm_nf_info *nf_info; - /* number of package between each print */ static uint32_t print_delay = 10000000; static uint16_t destination; -static uint8_t use_direct_rings = 0; -static uint8_t keep_running = 1; /*user defined packet size and destination mac address *size defaults to ethernet header length @@ -108,7 +103,8 @@ static uint64_t total_latency = 0; */ char *pcap_filename = NULL; -void nf_setup(struct onvm_nf_info *nf_info); +void +nf_setup(struct onvm_nf_local_ctx *nf_local_ctx); /* * Print a usage message @@ -116,19 +112,28 @@ void nf_setup(struct onvm_nf_info *nf_info); static void usage(const char *progname) { printf("Usage:\n"); - printf("%s [EAL args] -- [NF_LIB args] -- -d [-p ] " - "[-a] [-s ] [-m ] [-o ] " - "[-c ] [-l]\n", progname); + printf( + "%s [EAL args] -- [NF_LIB args] -- -d [-p ] " + "[-s ] [-m ] [-o ] " + "[-c ] [-l]\n", + progname); printf("%s -F [EAL args] -- [NF_LIB args] -- [NF args]\n\n", progname); printf("Flags:\n"); printf(" - `-d DST`: Destination Service ID to foward to\n"); - printf(" - `-a`: Use advanced rings interface instead of default `packet_handler`\n"); printf(" - `-p PRINT_DELAY`: Number of packets between each print, e.g. `-p 1` prints every packets.\n"); - printf(" - `-s PACKET_SIZE`: Size of packet, e.g. `-s 32` allocates 32 bytes for the data segment of `rte_mbuf`.\n"); - printf(" - `-m DEST_MAC`: User specified destination MAC address, e.g. `-m aa:bb:cc:dd:ee:ff` sets the destination address within the ethernet header that is located at the start of the packet data.\n"); + printf( + " - `-s PACKET_SIZE`: Size of packet, e.g. `-s 32` allocates 32 bytes for the data segment of " + "`rte_mbuf`.\n"); + printf( + " - `-m DEST_MAC`: User specified destination MAC address, e.g. `-m aa:bb:cc:dd:ee:ff` sets the " + "destination address within the ethernet header that is located at the start of the packet data.\n"); printf(" - `-o PCAP_FILENAME` : The filename of the pcap file to replay\n"); - printf(" - `-l LATENCY` : Enable latency measurement. This should only be enabled on one Speed Tester NF. Packets must be routed back to the same speed tester NF.\n"); - printf(" - `-c PACKET_NUMBER` : Use user specified number of packets in the batch. If not specified then this defaults to 128.\n"); + printf( + " - `-l LATENCY` : Enable latency measurement. This should only be enabled on one Speed Tester NF. Packets " + "must be routed back to the same speed tester NF.\n"); + printf( + " - `-c PACKET_NUMBER` : Use user specified number of packets in the batch. If not specified then this " + "defaults to 128.\n"); } /* @@ -139,80 +144,72 @@ parse_app_args(int argc, char *argv[], const char *progname) { int c, i, count, dst_flag = 0; int values[ETHER_ADDR_LEN]; - while ((c = getopt(argc, argv, "d:p:as:m:o:c:l")) != -1) { + while ((c = getopt(argc, argv, "d:p:s:m:o:c:l")) != -1) { switch (c) { - case 'a': - use_direct_rings = 1; - break; - case 'd': - destination = strtoul(optarg, NULL, 10); - dst_flag = 1; - break; - case 'p': - print_delay = strtoul(optarg, NULL, 10); - break; - case 's': - packet_size = strtoul(optarg, NULL, 10); - break; - case 'm': - count = sscanf(optarg, - "%x:%x:%x:%x:%x:%x", - &values[0], - &values[1], - &values[2], - &values[3], - &values[4], - &values[5]); - if (count == ETHER_ADDR_LEN) { - for (i = 0; i < ETHER_ADDR_LEN; ++i) { - d_addr_bytes[i] = (uint8_t) values[i]; + case 'd': + destination = strtoul(optarg, NULL, 10); + dst_flag = 1; + break; + case 'p': + print_delay = strtoul(optarg, NULL, 10); + break; + case 's': + packet_size = strtoul(optarg, NULL, 10); + break; + case 'm': + count = sscanf(optarg, "%x:%x:%x:%x:%x:%x", &values[0], &values[1], &values[2], + &values[3], &values[4], &values[5]); + if (count == ETHER_ADDR_LEN) { + for (i = 0; i < ETHER_ADDR_LEN; ++i) { + d_addr_bytes[i] = (uint8_t)values[i]; + } + } else { + usage(progname); + return -1; } - } else { - usage(progname); - return -1; - } - break; - case 'o': + break; + case 'o': #ifdef LIBPCAP - pcap_filename = strdup(optarg); - break; + pcap_filename = strdup(optarg); + break; #else - rte_exit(EXIT_FAILURE, "To enable pcap replay follow the README " - "instructins\n"); - break; + rte_exit(EXIT_FAILURE, + "To enable pcap replay follow the README " + "instructins\n"); + break; #endif - case 'c': - use_custom_pkt_count = 1; - packet_number = strtoul(optarg, NULL, 10); - if (packet_number > MAX_PKT_NUM) { - RTE_LOG(INFO, APP, "Illegal packet number(1 ~ %u) %u!\n", - MAX_PKT_NUM, packet_number); + case 'c': + use_custom_pkt_count = 1; + packet_number = strtoul(optarg, NULL, 10); + if (packet_number > MAX_PKT_NUM) { + RTE_LOG(INFO, APP, "Illegal packet number(1 ~ %u) %u!\n", MAX_PKT_NUM, + packet_number); + return -1; + } + break; + case 'l': + measure_latency = 1; + break; + case '?': + usage(progname); + if (optopt == 'd') + RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); + else if (optopt == 'p') + RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); + else if (optopt == 's') + RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); + else if (optopt == 'm') + RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); + else if (optopt == 'c') + RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); + else if (isprint(optopt)) + RTE_LOG(INFO, APP, "Unknown option `-%c'.\n", optopt); + else + RTE_LOG(INFO, APP, "Unknown option character `\\x%x'.\n", optopt); + return -1; + default: + usage(progname); return -1; - } - break; - case 'l': - measure_latency = 1; - break; - case '?': - usage(progname); - if (optopt == 'd') - RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); - else if (optopt == 'p') - RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); - else if (optopt == 's') - RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); - else if (optopt == 'm') - RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); - else if (optopt == 'c') - RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); - else if (isprint(optopt)) - RTE_LOG(INFO, APP, "Unknown option `-%c'.\n", optopt); - else - RTE_LOG(INFO, APP, "Unknown option character `\\x%x'.\n", optopt); - return -1; - default: - usage(progname); - return -1; } } @@ -231,12 +228,12 @@ parse_app_args(int argc, char *argv[], const char *progname) { * than one lcore enabled. */ static void -do_stats_display(struct rte_mbuf* pkt) { +do_stats_display(struct rte_mbuf *pkt) { static uint64_t last_cycles; static uint64_t cur_pkts = 0; static uint64_t last_pkts = 0; - const char clr[] = { 27, '[', '2', 'J', '\0' }; - const char topLeft[] = { 27, '[', '1', ';', '1', 'H', '\0' }; + const char clr[] = {27, '[', '2', 'J', '\0'}; + const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0'}; (void)pkt; uint64_t cur_cycles = rte_get_tsc_cycles(); @@ -245,12 +242,12 @@ do_stats_display(struct rte_mbuf* pkt) { /* Clear screen and move to top left */ printf("%s%s", clr, topLeft); - printf("Total packets: %9"PRIu64" \n", cur_pkts); - printf("TX pkts per second: %9"PRIu64" \n", (cur_pkts - last_pkts) - * rte_get_timer_hz() / (cur_cycles - last_cycles)); + printf("Total packets: %9" PRIu64 " \n", cur_pkts); + printf("TX pkts per second: %9" PRIu64 " \n", + (cur_pkts - last_pkts) * rte_get_timer_hz() / (cur_cycles - last_cycles)); if (measure_latency && latency_packets > 0) - printf("Avg latency nanoseconds: %6"PRIu64" \n", total_latency/(latency_packets) - * 1000000000 / rte_get_timer_hz()); + printf("Avg latency nanoseconds: %6" PRIu64 " \n", + total_latency / (latency_packets)*1000000000 / rte_get_timer_hz()); printf("Initial packets created: %u\n", packet_number); total_latency = 0; @@ -263,7 +260,8 @@ do_stats_display(struct rte_mbuf* pkt) { } static int -packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, __attribute__((unused)) struct onvm_nf_info *nf_info) { +packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, + __attribute__((unused)) struct onvm_nf_local_ctx *nf_local_ctx) { static uint32_t counter = 0; if (counter++ == print_delay) { do_stats_display(pkt); @@ -276,7 +274,7 @@ packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, __attribute__(( meta->action = ONVM_NF_ACTION_TONF; if (measure_latency && ONVM_CHECK_BIT(meta->flags, LATENCY_BIT)) { uint64_t curtime = rte_get_tsc_cycles(); - uint64_t* oldtime = (uint64_t *)(rte_pktmbuf_mtod(pkt, uint8_t *) + packet_size); + uint64_t *oldtime = (uint64_t *)(rte_pktmbuf_mtod(pkt, uint8_t *) + packet_size); if (*oldtime != 0) { total_latency += curtime - *oldtime; latency_packets++; @@ -290,85 +288,24 @@ packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, __attribute__(( return 0; } - -static void -handle_signal(int sig) { - if (sig == SIGINT || sig == SIGTERM) - keep_running = 0; -} - - -static void -run_advanced_rings(struct onvm_nf_info *nf_info) { - void *pkts[PKT_READ_SIZE]; - struct onvm_pkt_meta* meta; - uint16_t i, j, nb_pkts; - void *pktsTX[PKT_READ_SIZE]; - int tx_batch_size; - struct rte_ring *rx_ring; - struct rte_ring *tx_ring; - volatile struct onvm_nf *nf; - - printf("Process %d handling packets using advanced rings\n", nf_info->instance_id); - printf("[Press Ctrl-C to quit ...]\n"); - - /* Set core affinity depending on what we got from mgr */ - /* TODO as this is advanced ring mode it should have access to the core info struct */ - onvm_threading_core_affinitize(nf_info->core); - - /* Listen for ^C and docker stop so we can exit gracefully */ - signal(SIGINT, handle_signal); - signal(SIGTERM, handle_signal); - - /* Get rings from nflib */ - nf = onvm_nflib_get_nf(nf_info->instance_id); - rx_ring = nf->rx_q; - tx_ring = nf->tx_q; - - while (keep_running && rx_ring && tx_ring && nf) { - tx_batch_size = 0; - /* Dequeue all packets in ring up to max possible. */ - nb_pkts = rte_ring_dequeue_burst(rx_ring, pkts, PKT_READ_SIZE, NULL); - - if (unlikely(nb_pkts == 0)) { - continue; - } - /* Process all the packets */ - for (i = 0; i < nb_pkts; i++) { - meta = onvm_get_pkt_meta((struct rte_mbuf*)pkts[i]); - packet_handler((struct rte_mbuf*)pkts[i], meta, nf_info); - pktsTX[tx_batch_size++] = pkts[i]; - } - - if (unlikely(tx_batch_size > 0 && rte_ring_enqueue_bulk(tx_ring, pktsTX, tx_batch_size, NULL) == 0)) { - nf->stats.tx_drop += tx_batch_size; - for (j = 0; j < tx_batch_size; j++) { - rte_pktmbuf_free(pktsTX[j]); - } - } else { - nf->stats.tx += tx_batch_size; - } - } - onvm_nflib_stop(nf_info); -} - - /* * Generates fake packets or loads them from a pcap file */ void -nf_setup(struct onvm_nf_info *nf_info) { +nf_setup(struct onvm_nf_local_ctx *nf_local_ctx) { uint32_t i; + uint32_t pkts_generated; struct rte_mempool *pktmbuf_pool; + pkts_generated = 0; pktmbuf_pool = rte_mempool_lookup(PKTMBUF_POOL_NAME); if (pktmbuf_pool == NULL) { - onvm_nflib_stop(nf_info); + onvm_nflib_stop(nf_local_ctx); rte_exit(EXIT_FAILURE, "Cannot find mbuf pool!\n"); } #ifdef LIBPCAP - struct rte_mbuf* pkt; + struct rte_mbuf *pkt; pcap_t *pcap; const u_char *packet; struct pcap_pkthdr header; @@ -380,14 +317,17 @@ nf_setup(struct onvm_nf_info *nf_info) { pcap = pcap_open_offline(pcap_filename, errbuf); if (pcap == NULL) { fprintf(stderr, "Error reading pcap file: %s\n", errbuf); + onvm_nflib_stop(nf_local_ctx); rte_exit(EXIT_FAILURE, "Cannot open pcap file\n"); } - packet_number = (packet_number? packet_number : MAX_PKT_NUM); + packet_number = (use_custom_pkt_count ? packet_number : MAX_PKT_NUM); + struct rte_mbuf *pkts[packet_number]; + i = 0; - while (((packet = pcap_next(pcap, &header)) != NULL) && (i++ < packet_number)) { - struct onvm_pkt_meta* pmeta; + while (((packet = pcap_next(pcap, &header)) != NULL) && (i < packet_number)) { + struct onvm_pkt_meta *pmeta; struct onvm_ft_ipv4_5tuple key; pkt = rte_pktmbuf_alloc(pktmbuf_pool); @@ -398,7 +338,7 @@ nf_setup(struct onvm_nf_info *nf_info) { pkt->data_len = header.caplen; /* Copy the packet into the rte_mbuf data section */ - rte_memcpy(rte_pktmbuf_mtod(pkt, char*), packet, header.caplen); + rte_memcpy(rte_pktmbuf_mtod(pkt, char *), packet, header.caplen); pmeta = onvm_get_pkt_meta(pkt); pmeta->destination = destination; @@ -408,18 +348,21 @@ nf_setup(struct onvm_nf_info *nf_info) { onvm_ft_fill_key(&key, pkt); pkt->hash.rss = onvm_softrss(&key); - onvm_nflib_return_pkt(nf_info, pkt); + /* Add packet to batch, and update counter */ + pkts[i++] = pkt; + pkts_generated++; } + onvm_nflib_return_pkt_bulk(nf_local_ctx->nf, pkts, pkts_generated); } else { #endif /* use default number of initial packets if -c has not been used */ packet_number = (use_custom_pkt_count ? packet_number : DEFAULT_PKT_NUM); + struct rte_mbuf *pkts[packet_number]; printf("Creating %u packets to send to %u\n", packet_number, destination); - struct rte_mbuf *pkts[packet_number]; for (i = 0; i < packet_number; ++i) { - struct onvm_pkt_meta* pmeta; + struct onvm_pkt_meta *pmeta; struct ether_hdr *ehdr; int j; @@ -430,7 +373,7 @@ nf_setup(struct onvm_nf_info *nf_info) { } /*set up ether header and set new packet size*/ - ehdr = (struct ether_hdr *) rte_pktmbuf_append(pkt, packet_size); + ehdr = (struct ether_hdr *)rte_pktmbuf_append(pkt, packet_size); /*using manager mac addr for source *using input string for dest addr @@ -448,45 +391,67 @@ nf_setup(struct onvm_nf_info *nf_info) { pkt->hash.rss = i; pkt->port = 0; - if (measure_latency && (packet_number < DEFAULT_LAT_PKT_NUM || i % (packet_number/DEFAULT_LAT_PKT_NUM) == 0)) { + if (measure_latency && + (packet_number < DEFAULT_LAT_PKT_NUM || i % (packet_number / DEFAULT_LAT_PKT_NUM) == 0)) { pmeta->flags |= ONVM_SET_BIT(0, LATENCY_BIT); - uint64_t* ts = (uint64_t *) rte_pktmbuf_append(pkt, sizeof(uint64_t)); + uint64_t *ts = (uint64_t *)rte_pktmbuf_append(pkt, sizeof(uint64_t)); *ts = 0; } + + /* New packet generated successfully */ pkts[i] = pkt; + pkts_generated++; } - onvm_nflib_return_pkt_bulk(nf_info, pkts, packet_number); + onvm_nflib_return_pkt_bulk(nf_local_ctx->nf, pkts, pkts_generated); #ifdef LIBPCAP } #endif + /* Exit if packets were unexpectedly not created */ + if (pkts_generated == 0 && packet_number > 0) { + onvm_nflib_stop(nf_local_ctx); + rte_exit(EXIT_FAILURE, "Failed to create packets\n"); + } + + packet_number = pkts_generated; } -int main(int argc, char *argv[]) { +int +main(int argc, char *argv[]) { + struct onvm_nf_local_ctx *nf_local_ctx; + struct onvm_nf_function_table *nf_function_table; int arg_offset; + const char *progname = argv[0]; - if ((arg_offset = onvm_nflib_init(argc, argv, NF_TAG, &nf_info)) < 0) - return -1; + nf_local_ctx = onvm_nflib_init_nf_local_ctx(); + + onvm_nflib_start_signal_handler(nf_local_ctx, NULL); + + nf_function_table = onvm_nflib_init_nf_function_table(); + nf_function_table->pkt_handler = &packet_handler; + nf_function_table->setup = &nf_setup; + + if ((arg_offset = onvm_nflib_init(argc, argv, NF_TAG, nf_local_ctx, nf_function_table)) < 0) { + onvm_nflib_stop(nf_local_ctx); + if (arg_offset == ONVM_SIGNAL_TERMINATION) { + printf("Exiting due to user termination\n"); + return 0; + } else { + rte_exit(EXIT_FAILURE, "Failed ONVM init\n"); + } + } argc -= arg_offset; argv += arg_offset; if (parse_app_args(argc, argv, progname) < 0) { - onvm_nflib_stop(nf_info); + onvm_nflib_stop(nf_local_ctx); rte_exit(EXIT_FAILURE, "Invalid command-line arguments\n"); } - /* Set the function to execute before running the NF - * For advanced rings manually run the function */ - onvm_nflib_set_setup_function(nf_info, &nf_setup); + onvm_nflib_run(nf_local_ctx); - if (use_direct_rings) { - nf_setup(nf_info); - onvm_nflib_nf_ready(nf_info); - run_advanced_rings(nf_info); - } else { - onvm_nflib_run(nf_info, &packet_handler); - } + onvm_nflib_stop(nf_local_ctx); printf("If we reach here, program is ending\n"); return 0; } diff --git a/examples/start_nf.sh b/examples/start_nf.sh index 67de0ddc9..6e24d4607 100755 --- a/examples/start_nf.sh +++ b/examples/start_nf.sh @@ -42,13 +42,16 @@ if [ "$1" = "-F" ] then config=$2 shift 2 - CONFIG_ARGS="$@" - exec sudo $BINARY -F $config $CONFIG_ARGS + exec sudo $BINARY -F $config "$@" fi # Check if -- is present, if so parse dpdk/onvm specific args dash_dash_cnt=0 +non_nf_arg_cnt=0 for i in "$@" ; do + if [[ dash_dash_cnt -lt 2 ]] ; then + non_nf_arg_cnt=$((non_nf_arg_cnt+1)) + fi if [[ $i == "--" ]] ; then dash_dash_cnt=$((dash_dash_cnt+1)) fi @@ -58,7 +61,8 @@ done if [[ $dash_dash_cnt -ge 2 ]]; then DPDK_ARGS="$DPDK_BASE_ARGS $(echo " ""$@" | awk -F "--" '{print $1;}')" ONVM_ARGS="$(echo " ""$@" | awk -F "--" '{print $2;}')" - NF_ARGS="$(echo " ""$@" | awk -F "--" '{print $3;}')" + # Move to NF arguments + shift ${non_nf_arg_cnt} elif [[ $dash_dash_cnt -eq 0 ]]; then # Dealing with required args shared by all NFs service=$1 @@ -66,7 +70,6 @@ elif [[ $dash_dash_cnt -eq 0 ]]; then DPDK_ARGS="-l $DEFAULT_CORE_ID $DPDK_BASE_ARGS" ONVM_ARGS="-r $service" - NF_ARGS="$@" elif [[ $dash_dash_cnt -eq 1 ]]; then # Don't allow only one `--` echo "This script expects 0 or at least 2 '--' argument separators" @@ -74,4 +77,4 @@ elif [[ $dash_dash_cnt -eq 1 ]]; then exit 1 fi -exec sudo $BINARY $DPDK_ARGS -- $ONVM_ARGS -- $NF_ARGS +exec sudo $BINARY $DPDK_ARGS -- $ONVM_ARGS -- "$@" diff --git a/examples/test_flow_dir/test_flow_dir.c b/examples/test_flow_dir/test_flow_dir.c index a66e21f52..e9b61876d 100644 --- a/examples/test_flow_dir/test_flow_dir.c +++ b/examples/test_flow_dir/test_flow_dir.c @@ -5,8 +5,8 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,37 +38,34 @@ * test_flow_dir.c - an example using onvm_flow_dir_* APIs. ********************************************************************/ -#include -#include -#include +#include +#include +#include #include #include -#include -#include +#include +#include #include -#include #include -#include +#include +#include #include -#include #include #include -#include +#include #include +#include +#include "onvm_flow_dir.h" +#include "onvm_flow_table.h" #include "onvm_nflib.h" #include "onvm_pkt_helper.h" -#include "onvm_flow_table.h" #include "onvm_sc_common.h" -#include "onvm_flow_dir.h" #include "onvm_sc_mgr.h" #define NF_TAG "test_flow_dir" -/* Struct that contains information about this NF */ -struct onvm_nf_info *nf_info; - /* number of package between each print */ static uint32_t print_delay = 1000000; @@ -96,26 +93,26 @@ parse_app_args(int argc, char *argv[], const char *progname) { while ((c = getopt(argc, argv, "d:p:")) != -1) { switch (c) { - case 'd': - destination = strtoul(optarg, NULL, 10); - break; - case 'p': - print_delay = strtoul(optarg, NULL, 10); - break; - case '?': - usage(progname); - if (optopt == 'd') - RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); - else if (optopt == 'p') - RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); - else if (isprint(optopt)) - RTE_LOG(INFO, APP, "Unknown option `-%c'.\n", optopt); - else - RTE_LOG(INFO, APP, "Unknown option character `\\x%x'.\n", optopt); - return -1; - default: - usage(progname); - return -1; + case 'd': + destination = strtoul(optarg, NULL, 10); + break; + case 'p': + print_delay = strtoul(optarg, NULL, 10); + break; + case '?': + usage(progname); + if (optopt == 'd') + RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); + else if (optopt == 'p') + RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); + else if (isprint(optopt)) + RTE_LOG(INFO, APP, "Unknown option `-%c'.\n", optopt); + else + RTE_LOG(INFO, APP, "Unknown option character `\\x%x'.\n", optopt); + return -1; + default: + usage(progname); + return -1; } } return optind; @@ -128,11 +125,11 @@ parse_app_args(int argc, char *argv[], const char *progname) { * than one lcore enabled. */ static void -do_stats_display(struct rte_mbuf* pkt) { - const char clr[] = { 27, '[', '2', 'J', '\0' }; - const char topLeft[] = { 27, '[', '1', ';', '1', 'H', '\0' }; +do_stats_display(struct rte_mbuf *pkt) { + const char clr[] = {27, '[', '2', 'J', '\0'}; + const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0'}; static uint64_t pkt_process = 0; - struct ipv4_hdr* ip; + struct ipv4_hdr *ip; pkt_process += print_delay; @@ -143,7 +140,7 @@ do_stats_display(struct rte_mbuf* pkt) { printf("-----\n"); printf("Port : %d\n", pkt->port); printf("Size : %d\n", pkt->pkt_len); - printf("N° : %"PRIu64"\n", pkt_process); + printf("N° : %" PRIu64 "\n", pkt_process); printf("\n\n"); ip = onvm_pkt_ipv4_hdr(pkt); @@ -155,53 +152,71 @@ do_stats_display(struct rte_mbuf* pkt) { } static int -packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, __attribute__((unused)) struct onvm_nf_info *nf_info) { +packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, + __attribute__((unused)) struct onvm_nf_local_ctx *nf_local_ctx) { static uint32_t counter = 0; - struct onvm_flow_entry *flow_entry = NULL; - int ret; + struct onvm_flow_entry *flow_entry = NULL; + int ret; - if (++counter == print_delay) { + if (++counter == print_delay) { do_stats_display(pkt); counter = 0; } - ret = onvm_flow_dir_get_pkt(pkt, &flow_entry); - if (ret >= 0) { - meta->action = ONVM_NF_ACTION_NEXT; - } else { - ret = onvm_flow_dir_add_pkt(pkt, &flow_entry); - if (ret < 0) { - meta->action = ONVM_NF_ACTION_DROP; - meta->destination = 0; - return 0; - } - memset(flow_entry, 0, sizeof(struct onvm_flow_entry)); - flow_entry->sc = onvm_sc_create(); - onvm_sc_append_entry(flow_entry->sc, ONVM_NF_ACTION_TONF, destination); - //onvm_sc_print(flow_entry->sc); - } - return 0; + ret = onvm_flow_dir_get_pkt(pkt, &flow_entry); + if (ret >= 0) { + meta->action = ONVM_NF_ACTION_NEXT; + } else { + ret = onvm_flow_dir_add_pkt(pkt, &flow_entry); + if (ret < 0) { + meta->action = ONVM_NF_ACTION_DROP; + meta->destination = 0; + return 0; + } + memset(flow_entry, 0, sizeof(struct onvm_flow_entry)); + flow_entry->sc = onvm_sc_create(); + onvm_sc_append_entry(flow_entry->sc, ONVM_NF_ACTION_TONF, destination); + // onvm_sc_print(flow_entry->sc); + } + return 0; } -int main(int argc, char *argv[]) { +int +main(int argc, char *argv[]) { int arg_offset; - + struct onvm_nf_local_ctx *nf_local_ctx; + struct onvm_nf_function_table *nf_function_table; const char *progname = argv[0]; - if ((arg_offset = onvm_nflib_init(argc, argv, NF_TAG, &nf_info)) < 0) - return -1; + nf_local_ctx = onvm_nflib_init_nf_local_ctx(); + onvm_nflib_start_signal_handler(nf_local_ctx, NULL); + + nf_function_table = onvm_nflib_init_nf_function_table(); + nf_function_table->pkt_handler = &packet_handler; + + if ((arg_offset = onvm_nflib_init(argc, argv, NF_TAG, nf_local_ctx, nf_function_table)) < 0) { + onvm_nflib_stop(nf_local_ctx); + if (arg_offset == ONVM_SIGNAL_TERMINATION) { + printf("Exiting due to user termination\n"); + return 0; + } else { + rte_exit(EXIT_FAILURE, "Failed ONVM init\n"); + } + } + argc -= arg_offset; argv += arg_offset; - destination = nf_info->service_id + 1; + destination = nf_local_ctx->nf->service_id + 1; if (parse_app_args(argc, argv, progname) < 0) rte_exit(EXIT_FAILURE, "Invalid command-line arguments\n"); - /* Map the sdn_ft table */ - onvm_flow_dir_nf_init(); + /* Map the sdn_ft table */ + onvm_flow_dir_nf_init(); - onvm_nflib_run(nf_info, &packet_handler); - printf("If we reach here, program is ending\n"); + onvm_nflib_run(nf_local_ctx); - return 0; + onvm_nflib_stop(nf_local_ctx); + printf("If we reach here, program is ending\n"); + return 0; } diff --git a/onvm/README.md b/onvm/README.md index 3138465f1..7abbe0923 100644 --- a/onvm/README.md +++ b/onvm/README.md @@ -7,46 +7,59 @@ Manager The openNetVM manager is responsible for orchestrating traffic between NFs. It handles all Rx/Tx traffic in and out of the system, dynamically manages NFs starting and stopping, and it displays statistics regarding all traffic. ``` -$sudo ./onvm_mgr/onvm_mgr/x86_64-native-linuxapp-gcc/onvm_mgr -l CORELIST -n MEMORY_CHANNELS --proc-type=primary -- -p PORTMASK -n NF_COREMASK [-r NUM_SERVICES] [-d DEFAULT_SERVICE] [-s STATS_OUTPUT] +$sudo ./onvm_mgr/onvm_mgr/x86_64-native-linuxapp-gcc/onvm_mgr -l CORELIST -n MEMORY_CHANNELS --proc-type=primary -- -p PORTMASK -n NF_COREMASK [-r NUM_SERVICES] [-d DEFAULT_SERVICE] [-s STATS_OUTPUT] [-t TIME_TO_LIVE] [-l PACKET_LIMIT] Options: - -c an hexadecimal bit mask of the cores to run on. + -c an hexadecimal bit mask of the cores to run on. - -n number of memory channels per processor socket. + -n number of memory channels per processor socket. - -l comma deliniated list of cores to run on. + -l comma deliniated list of cores to run on. - –lcores map lcore set to physical cpu set. + –lcores map lcore set to physical cpu set. - -m memory to allocate. + -m memory to allocate. - -r set the number of memory ranks (auto-detected by default). + -r set the number of memory ranks (auto-detected by default). - -v display the version information on startup. + -v display the version information on startup. - -syslog set the syslog facility. + -syslog set the syslog facility. - --socket_mem set the memory to allocate on specific sockets. + --socket_mem set the memory to allocate on specific sockets. - --huge-dir directory where the hugetlbfs is mounted. + --huge-dir directory where the hugetlbfs is mounted. - -–proc-type set the type of the current process. + -–proc-type set the type of the current process. - -- secondary commands + -- secondary commands - -p a hexadecimal bit mask of the ports to use. + -p a hexadecimal bit mask of the ports to use. - -n a hexadecimal bit mask of cores for NFs to run on. + -n a hexadecimal bit mask of cores for NFs to run on. - -r an integer specifying the number of services. + -r an integer specifying the number of services. - -d an integer specifying the default service id. + -d an integer specifying the default service id. - -s a string (stdout/stderr/web) specifying where to -output statistics. + -s a string (stdout/stderr/web) specifying where to + output statistics. + + -v an integer specifying verbosity level + + -c flag to enable shared_cpu mode + + -t an integer specifying the time to live in seconds + + -l an integer specifying the RX packet limit in + Millions of pkts ``` +Usage +-- +If the NFs crash with an error similar to `Cannot mmap memory for rte_config at [0x7ffff7ff3000], got [0x7ffff7ff2000] - please use '--base-virtaddr' option`, please use the `-a 0x7f000000000` flag for the onvm_mgr, this will resolve the issue. + NF Library -- The NF Library is responsible for providing an interface for NFs to communicate with the manager. It provides functions to initialize and send/receive packets to and from the manager. This library provides the manager with a function pointer to the NF's `packet_handler`. @@ -55,4 +68,85 @@ Packet Helper Library -- The Packet Helper Libary provides an interface to extract TCP/IP, UDP, and other packet headers that were lost due to [Intel DPDK][dpdk]. Since DPDK avoides the Linux Kernel to bring packet data into userspace, we lose the encapsulation and decapsulation that the Kernel provides. DPDK wraps packet data inside its own structure, the `rte_mbuf`. +Stats +-- + +There are 2 ways to view onvm_mgr stats: + +1. Console stats provide a detailed overview of all statistics and provide different verbosity modes. + + The default mode displays only the most critical NF information: + ``` + PORTS + ----- + Port 0: '90:e2:ba:b3:bc:6c' + + Port 0 - rx: 4 ( 0 pps) tx: 0 ( 0 pps) + + NF TAG IID / SID / CORE rx_pps / tx_pps rx_drop / tx_drop out / tonf / drop + ---------------------------------------------------------------------------------------------------------------------- + speed_tester 1 / 1 / 4 1693920 / 1693920 0 / 0 0 / 40346970 / 0 + ``` + + The verbose mode (`-v`) adds `PNT`(Parent ID), `S|W`(NF state, sleeping or working), `CHLD`(Children count), as well as additional packet processing information: + ``` + PORTS + ----- + Port 0: '90:e2:ba:b3:bc:6c' + + Port 0 - rx: 4 ( 0 pps) tx: 0 ( 0 pps) + + NF TAG IID / SID / CORE rx_pps / tx_pps rx_drop / tx_drop out / tonf / drop + PNT / S|W / CHLD drop_pps / drop_pps rx_drop / tx_drop next / buf / ret + ---------------------------------------------------------------------------------------------------------------------- + speed_tester 1 / 1 / 4 9661664 / 9661664 94494528 / 94494528 0 / 94494487 / 0 + 0 / W / 0 0 / 0 0 / 0 0 / 0 / 128 + ``` + + The shared core mode (`-c`) adds wakeup information stats: + ``` + PORTS + ----- + Port 0: '90:e2:ba:b3:bc:6c' + + Port 0 - rx: 5 ( 0 pps) tx: 0 ( 0 pps) + + NF TAG IID / SID / CORE rx_pps / tx_pps rx_drop / tx_drop out / tonf / drop + PNT / S|W / CHLD drop_pps / drop_pps rx_drop / tx_drop next / buf / ret + wakeups / wakeup_rt + ---------------------------------------------------------------------------------------------------------------------- + simple_forward 2 / 2 / 4 27719 / 27719 764439 / 764439 0 / 764439 / 0 + 0 / S / 0 0 / 0 0 / 0 0 / 0 / 0 + 730557 / 25344 + + speed_tester 3 / 1 / 5 27719 / 27719 764440 / 764439 0 / 764440 / 0 + 0 / W / 0 0 / 0 0 / 0 0 / 0 / 1 + 730560 / 25347 + + + + Shared core stats + ----------------- + Total wakeups = 1461122, Wakeup rate = 50696 + ``` + + The super verbose stats mode (`-vv`) dumps all stats in a comma separated list for easy script parsing: + ``` + #YYYY-MM-DD HH:MM:SS,nic_rx_pkts,nic_rx_pps,nic_tx_pkts,nic_tx_pps + #YYYY-MM-DD HH:MM:SS,nf_tag,instance_id,service_id,core,parent,state,children_cnt,rx,tx,rx_pps,tx_pps,rx_drop,tx_drop,rx_drop_rate,tx_drop_rate,act_out,act_tonf,act_drop,act_next,act_buffer,act_returned,num_wakeups,wakeup_rate + 2019-06-04 08:54:52,0,4,4,0,0 + 2019-06-04 08:54:53,0,4,0,0,0 + 2019-06-04 08:54:54,simple_forward,1,2,4,0,W,0,29058,29058,29058,29058,0,0,0,0,0,29058,0,0,0,0,28951,28951 + 2019-06-04 08:54:54,speed_tester,2,1,5,0,S,0,29058,29058,29058,29058,0,0,0,0,0,29059,0,0,0,1,28952,28952 + 2019-06-04 08:54:55,0,4,0,0,0 + 2019-06-04 08:54:55,simple_forward,1,2,4,0,W,0,101844,101843,72785,72785,0,0,0,0,0,101843,0,0,0,0,101660,101660 + 2019-06-04 08:54:55,speed_tester,2,1,5,0,W,0,101844,101843,72785,72785,0,0,0,0,0,101844,0,0,0,1,101660,101660 + ``` + + +2. Web Stats provide an easy to navigate web view with NF performance graphs, manager port stats and the core layout across the system. It also keeps track of timestamps for NF events such as NF_STARTING and NF_STOPPING. + + For more info and design details check the [web stats docs][web_stats_docs] + [dpdk]: http://dpdk.org/ +[web_stats_docs]: ../onvm_web/README.md diff --git a/onvm/go.sh b/onvm/go.sh index 4d3034e97..540fb08df 100755 --- a/onvm/go.sh +++ b/onvm/go.sh @@ -12,9 +12,15 @@ function usage { echo -e "\tRuns OVNM the same as above, but runs the web stats on port 9000 instead of defaulting to 8080" echo -e "$0 0,1,2,3 3 0xF0 -s stdout" echo -e "\tRuns ONVM the same way as above, but prints statistics to stdout" - echo -e "$0 0,1,2,3 3 0xF0 -s -v stdout" + echo -e "$0 0,1,2,3 3 0xF0 -s stdout -c" + echo -e "\tRuns ONVM the same way as above, but enables shared cpu support" + echo -e "$0 0,1,2,3 3 0xF0 -s stdout -t 42" + echo -e "\tRuns ONVM the same way as above, but shuts down after 42 seconds" + echo -e "$0 0,1,2,3 3 0xF0 -s stdout -l 64" + echo -e "\tRuns ONVM the same way as above, but shuts down after receiving 64 million packets" + echo -e "$0 0,1,2,3 3 0xF0 -s stdout -v" echo -e "\tRuns ONVM the same way as above, but prints statistics to stdout in extra verbose mode" - echo -e "$0 0,1,2,3 3 0xF0 -s stdout" + echo -e "$0 0,1,2,3 3 0xF0 -s stdout -vv" echo -e "\tRuns ONVM the same way as above, but prints statistics to stdout in raw data dump mode" echo -e "$0 0,1,2,3 3 0xF0 -a 0x7f000000000 -s stdout" echo -e "\tRuns ONVM the same way as above, but adds a --base-virtaddr dpdk parameter" @@ -38,14 +44,17 @@ then usage fi -while getopts "a:r:d:s:p:z:v" opt; do +while getopts "a:r:d:s:t:l:p:z:cv" opt; do case $opt in a) virt_addr="--base-virtaddr=$OPTARG";; r) num_srvc="-r $OPTARG";; d) def_srvc="-d $optarg";; s) stats="-s $OPTARG";; + t) ttl="-t $OPTARG";; + l) packet_limit="-l $OPTARG";; p) web_port="$OPTARG";; z) stats_sleep_time="-z $OPTARG";; + c) shared_cpu_flag="-c";; v) verbosity=$(($verbosity+1));; \?) echo "Unknown option -$OPTARG" && usage ;; @@ -68,7 +77,7 @@ then fi sudo rm -rf /mnt/huge/rtemap_* -sudo $SCRIPTPATH/onvm_mgr/$RTE_TARGET/onvm_mgr -l $cpu -n 4 --proc-type=primary ${virt_addr} -- -p ${ports} -n ${nf_cores} ${num_srvc} ${def_srvc} ${stats} ${stats_sleep_time} ${verbosity_level} +sudo $SCRIPTPATH/onvm_mgr/$RTE_TARGET/onvm_mgr -l $cpu -n 4 --proc-type=primary ${virt_addr} -- -p ${ports} -n ${nf_cores} ${num_srvc} ${def_srvc} ${stats} ${stats_sleep_time} ${verbosity_level} ${ttl} ${packet_limit} ${shared_cpu_flag} if [ "${stats}" = "-s web" ] then diff --git a/onvm/lib/cJSON.c b/onvm/lib/cJSON.c index 12e78cf29..a01d31936 100644 --- a/onvm/lib/cJSON.c +++ b/onvm/lib/cJSON.c @@ -633,14 +633,17 @@ static const char *parse_string(cJSON *item, const char *str, const char **ep) /* 10xxxxxx */ *--ptr2 = ((uc | 0x80) & 0xBF); uc >>= 6; + /* FALLTHROUGH */ case 3: /* 10xxxxxx */ *--ptr2 = ((uc | 0x80) & 0xBF); uc >>= 6; + /* FALLTHROUGH */ case 2: /* 10xxxxxx */ *--ptr2 = ((uc | 0x80) & 0xBF); uc >>= 6; + /* FALLTHROUGH */ case 1: /* depending on the length in bytes this determines the * encoding ofthe first UTF8 byte */ diff --git a/onvm/onvm_mgr/main.c b/onvm/onvm_mgr/main.c index fcee4e5c9..ba8655fcf 100644 --- a/onvm/onvm_mgr/main.c +++ b/onvm/onvm_mgr/main.c @@ -5,10 +5,10 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside - * 2010-2014 Intel Corporation. All rights reserved. - * 2016-2017 Hewlett Packard Enterprise Development LP + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside + * 2010-2019 Intel Corporation. All rights reserved. + * 2016-2019 Hewlett Packard Enterprise Development LP * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -39,7 +39,6 @@ * ********************************************************************/ - /****************************************************************************** main.c @@ -48,14 +47,12 @@ ******************************************************************************/ - #include #include "onvm_mgr.h" -#include "onvm_stats.h" -#include "onvm_pkt.h" #include "onvm_nf.h" - +#include "onvm_pkt.h" +#include "onvm_stats.h" /****************************Internal Declarations****************************/ @@ -68,7 +65,8 @@ static uint8_t main_keep_running = 1; // race the stats display to be able to print, so keep this varable separate static uint8_t worker_keep_running = 1; -static void handle_signal(int sig); +static void +handle_signal(int sig); /*******************************Worker threads********************************/ @@ -81,6 +79,10 @@ master_thread_main(void) { int shutdown_iter_count; const unsigned sleeptime = global_stats_sleep_time; const unsigned verbosity_level = global_verbosity_level; + const uint32_t time_to_live = global_time_to_live; + const uint32_t pkt_limit = global_pkt_limit; + const uint64_t start_time = rte_get_tsc_cycles(); + uint64_t total_rx_pkts; RTE_LOG(INFO, APP, "Core %d: Running master thread\n", rte_lcore_id()); @@ -89,16 +91,36 @@ master_thread_main(void) { } RTE_LOG(INFO, APP, "Stats verbosity level = %d\n", verbosity_level); + if (time_to_live) + RTE_LOG(INFO, APP, "Manager time to live = %u\n", global_time_to_live); + if (pkt_limit) + RTE_LOG(INFO, APP, "Manager packet limit = %u\n", global_pkt_limit); /* Initial pause so above printf is seen */ sleep(5); onvm_stats_init(verbosity_level); /* Loop forever: sleep always returns 0 or <= param */ - while ( main_keep_running && sleep(sleeptime) <= sleeptime) { + while (main_keep_running && sleep(sleeptime) <= sleeptime) { onvm_nf_check_status(); if (stats_destination != ONVM_STATS_NONE) onvm_stats_display_all(sleeptime, verbosity_level); + + if (time_to_live && unlikely((rte_get_tsc_cycles() - start_time) * TIME_TTL_MULTIPLIER / + rte_get_timer_hz() >= time_to_live)) { + printf("Time to live exceeded, shutting down\n"); + main_keep_running = 0; + } + + if (pkt_limit) { + total_rx_pkts = 0; + for (i = 0; i < ports->num_ports; i++) + total_rx_pkts += ports->rx_stats.rx[ports->id[i]]; + if (unlikely(total_rx_pkts >= (uint64_t) pkt_limit * PKT_TTL_MULTIPLIER)) { + printf("Packet limit exceeded, shutting down\n"); + main_keep_running = 0; + } + } } /* Close out file references and things */ @@ -115,51 +137,61 @@ master_thread_main(void) { /* Tell all NFs to stop */ for (i = 0; i < MAX_NFS; i++) { - if (nfs[i].info == NULL) { + if (nfs[i].status != NF_RUNNING) continue; - } - RTE_LOG(INFO, APP, "Core %d: Notifying NF %"PRIu16" to shut down\n", rte_lcore_id(), i); + + RTE_LOG(INFO, APP, "Core %d: Notifying NF %" PRIu16 " to shut down\n", rte_lcore_id(), i); onvm_nf_send_msg(i, MSG_STOP, NULL); + + /* If in shared core mode NFs might be sleeping */ + if (ONVM_NF_SHARE_CORES && rte_atomic16_read(nf_wakeup_infos[i].shm_server) == 1) { + nf_wakeup_infos[i].num_wakeups++; + rte_atomic16_set(nf_wakeup_infos[i].shm_server, 0); + sem_post(nf_wakeup_infos[i].mutex); + } } /* Wait to process all exits */ - for (shutdown_iter_count = 0; - shutdown_iter_count < MAX_SHUTDOWN_ITERS && num_nfs > 0; - shutdown_iter_count++) { + for (shutdown_iter_count = 0; shutdown_iter_count < MAX_SHUTDOWN_ITERS && num_nfs > 0; shutdown_iter_count++) { onvm_nf_check_status(); - RTE_LOG(INFO, APP, "Core %d: Waiting for %"PRIu16" NFs to exit\n", rte_lcore_id(), num_nfs); + RTE_LOG(INFO, APP, "Core %d: Waiting for %" PRIu16 " NFs to exit\n", rte_lcore_id(), num_nfs); sleep(sleeptime); } if (num_nfs > 0) { - RTE_LOG(INFO, APP, "Core %d: Up to %"PRIu16" NFs may still be running and must be killed manually\n", rte_lcore_id(), num_nfs); + RTE_LOG(INFO, APP, "Core %d: Up to %" PRIu16 " NFs may still be running and must be killed manually\n", + rte_lcore_id(), num_nfs); + } + + /* Clean up the shared memory */ + if (ONVM_NF_SHARE_CORES) { + for (i = 0; i < MAX_NFS; i++) { + sem_close(nf_wakeup_infos[i].mutex); + sem_unlink(nf_wakeup_infos[i].sem_name); + } } RTE_LOG(INFO, APP, "Core %d: Master thread done\n", rte_lcore_id()); } - /* * Function to receive packets from the NIC * and distribute them to the default service */ static int rx_thread_main(void *arg) { - uint16_t i, rx_count; + uint16_t i, rx_count, cur_lcore; struct rte_mbuf *pkts[PACKET_READ_SIZE]; - struct queue_mgr *rx_mgr = (struct queue_mgr*)arg; + struct queue_mgr *rx_mgr = (struct queue_mgr *)arg; + cur_lcore = rte_lcore_id(); - RTE_LOG(INFO, - APP, - "Core %d: Running RX thread for RX queue %d\n", - rte_lcore_id(), - rx_mgr->id); + onvm_stats_gen_event_info("Rx Start", ONVM_EVENT_WITH_CORE, &cur_lcore); + RTE_LOG(INFO, APP, "Core %d: Running RX thread for RX queue %d\n", cur_lcore, rx_mgr->id); for (; worker_keep_running;) { /* Read ports */ for (i = 0; i < ports->num_ports; i++) { - rx_count = rte_eth_rx_burst(ports->id[i], rx_mgr->id, \ - pkts, PACKET_READ_SIZE); + rx_count = rte_eth_rx_burst(ports->id[i], rx_mgr->id, pkts, PACKET_READ_SIZE); ports->rx_stats.rx[ports->id[i]] += rx_count; /* Now process the NIC packets read */ @@ -176,30 +208,26 @@ rx_thread_main(void *arg) { RTE_LOG(INFO, APP, "Core %d: RX thread done\n", rte_lcore_id()); + free(rx_mgr->nf_rx_bufs); + free(rx_mgr); return 0; } - static int tx_thread_main(void *arg) { struct onvm_nf *nf; - unsigned i, tx_count; + unsigned i, tx_count, cur_lcore; struct rte_mbuf *pkts[PACKET_READ_SIZE]; struct queue_mgr *tx_mgr = (struct queue_mgr *)arg; + cur_lcore = rte_lcore_id(); + onvm_stats_gen_event_info("Tx Start", ONVM_EVENT_WITH_CORE, &cur_lcore); if (tx_mgr->tx_thread_info->first_nf == tx_mgr->tx_thread_info->last_nf - 1) { - RTE_LOG(INFO, - APP, - "Core %d: Running TX thread for NF %d\n", - rte_lcore_id(), + RTE_LOG(INFO, APP, "Core %d: Running TX thread for NF %d\n", cur_lcore, tx_mgr->tx_thread_info->first_nf); } else if (tx_mgr->tx_thread_info->first_nf < tx_mgr->tx_thread_info->last_nf) { - RTE_LOG(INFO, - APP, - "Core %d: Running TX thread for NFs %d to %d\n", - rte_lcore_id(), - tx_mgr->tx_thread_info->first_nf, - tx_mgr->tx_thread_info->last_nf-1); + RTE_LOG(INFO, APP, "Core %d: Running TX thread for NFs %d to %d\n", cur_lcore, + tx_mgr->tx_thread_info->first_nf, tx_mgr->tx_thread_info->last_nf - 1); } for (; worker_keep_running;) { @@ -209,8 +237,8 @@ tx_thread_main(void *arg) { if (!onvm_nf_is_valid(nf)) continue; - /* Dequeue all packets in ring up to max possible. */ - tx_count = rte_ring_dequeue_burst(nf->tx_q, (void **) pkts, PACKET_READ_SIZE, NULL); + /* Dequeue all packets in ring up to max possible. */ + tx_count = rte_ring_dequeue_burst(nf->tx_q, (void **)pkts, PACKET_READ_SIZE, NULL); /* Now process the Client packets read */ if (likely(tx_count > 0)) { @@ -227,6 +255,10 @@ tx_thread_main(void *arg) { RTE_LOG(INFO, APP, "Core %d: TX thread done\n", rte_lcore_id()); + free(tx_mgr->tx_thread_info->port_tx_bufs); + free(tx_mgr->tx_thread_info); + free(tx_mgr->nf_rx_bufs); + free(tx_mgr); return 0; } @@ -237,38 +269,90 @@ handle_signal(int sig) { } } +static inline void +wakeup_client(struct nf_wakeup_info *nf_wakeup_info) { + nf_wakeup_info->num_wakeups++; + rte_atomic16_set(nf_wakeup_info->shm_server, 0); + sem_post(nf_wakeup_info->mutex); +} -/*******************************Main function*********************************/ +static int +wakeup_thread_main(void *arg) { + unsigned i; + struct onvm_nf *nf; + struct nf_wakeup_info *nf_wakeup_info; + struct wakeup_thread_context *wakeup_ctx = (struct wakeup_thread_context *)arg; + + if (wakeup_ctx->first_nf == wakeup_ctx->last_nf - 1) { + RTE_LOG(INFO, APP, "Core %d: Running Wakeup thread for NF %d\n", rte_lcore_id(), + wakeup_ctx->first_nf); + } else if (wakeup_ctx->first_nf < wakeup_ctx->last_nf) { + RTE_LOG(INFO, APP, "Core %d: Running Wakeup thread for NFs %d to %d\n", rte_lcore_id(), + wakeup_ctx->first_nf, wakeup_ctx->last_nf - 1); + } + + for (; worker_keep_running;) { + for (i = wakeup_ctx->first_nf; i < wakeup_ctx->last_nf; i++) { + nf = &nfs[i]; + nf_wakeup_info = &nf_wakeup_infos[i]; + if (!onvm_nf_is_valid(nf)) + continue; + /* Check if NF is sleeping and has pkts on the rx queue */ + if (!whether_wakeup_client(nf, nf_wakeup_info)) + continue; + + wakeup_client(nf_wakeup_info); + } + } + + free(wakeup_ctx); + return 0; +} + +/*******************************Main function*********************************/ int main(int argc, char *argv[]) { - unsigned cur_lcore, rx_lcores, tx_lcores; - unsigned nfs_per_tx; + unsigned cur_lcore, rx_lcores, tx_lcores, wakeup_lcores; + unsigned nfs_per_tx, nfs_per_wakeup_thread; unsigned i; /* initialise the system */ - - /* Reserve ID 0 for internal manager things */ - next_instance_id = 1; - if (init(argc, argv) < 0 ) + if (init(argc, argv) < 0) return -1; + RTE_LOG(INFO, APP, "Finished Process Init.\n"); /* clear statistics */ onvm_stats_clear_all_nfs(); - /* Reserve n cores for: ONVM_NUM_MGR_AUX_THREADS for auxiliary(f.e. stats), ONVM_NUM_RX_THREADS for Rx, and all remaining for Tx */ + /* Reserve n cores for: ONVM_NUM_MGR_AUX_THREADS for auxiliary(f.e. stats), ONVM_NUM_RX_THREADS for Rx, and all + * remaining for Tx (subtract wakeup cores if shared core mode is enabled) */ cur_lcore = rte_lcore_id(); rx_lcores = ONVM_NUM_RX_THREADS; tx_lcores = rte_lcore_count() - rx_lcores - ONVM_NUM_MGR_AUX_THREADS; + /* If shared core mode enabled adjust core numbers */ + if (ONVM_NF_SHARE_CORES) { + wakeup_lcores = ONVM_NUM_WAKEUP_THREADS; + tx_lcores -= wakeup_lcores; + if (tx_lcores < 1) { + RTE_LOG(INFO, APP, "Not enough cores to enabled shared core support\n"); + return -1; + } + } + + onvm_stats_gen_event_info("MGR Start", ONVM_EVENT_WITH_CORE, &cur_lcore); + /* Offset cur_lcore to start assigning TX cores */ - cur_lcore += (rx_lcores-1); + cur_lcore += (rx_lcores - 1); RTE_LOG(INFO, APP, "%d cores available in total\n", rte_lcore_count()); RTE_LOG(INFO, APP, "%d cores available for handling manager RX queues\n", rx_lcores); RTE_LOG(INFO, APP, "%d cores available for handling TX queues\n", tx_lcores); + if (ONVM_NF_SHARE_CORES) + RTE_LOG(INFO, APP, "%d cores available for handling wakeup\n", wakeup_lcores); RTE_LOG(INFO, APP, "%d cores available for handling stats\n", 1); /* Evenly assign NFs to TX threads */ @@ -280,7 +364,7 @@ main(int argc, char *argv[]) { * We want to distribute the number of running NFs across available * TX threads */ - nfs_per_tx = ceil((float)MAX_NFS/tx_lcores); + nfs_per_tx = ceil((float)MAX_NFS / tx_lcores); // We start the system with 0 NFs active num_nfs = 0; @@ -297,13 +381,10 @@ main(int argc, char *argv[]) { tx_mgr->tx_thread_info->port_tx_bufs = calloc(RTE_MAX_ETHPORTS, sizeof(struct packet_buf)); tx_mgr->nf_rx_bufs = calloc(MAX_NFS, sizeof(struct packet_buf)); tx_mgr->tx_thread_info->first_nf = RTE_MIN(i * nfs_per_tx + 1, (unsigned)MAX_NFS); - tx_mgr->tx_thread_info->last_nf = RTE_MIN((i+1) * nfs_per_tx + 1, (unsigned)MAX_NFS); + tx_mgr->tx_thread_info->last_nf = RTE_MIN((i + 1) * nfs_per_tx + 1, (unsigned)MAX_NFS); cur_lcore = rte_get_next_lcore(cur_lcore, 1, 1); - if (rte_eal_remote_launch(tx_thread_main, (void*)tx_mgr, cur_lcore) == -EBUSY) { - RTE_LOG(ERR, - APP, - "Core %d is already busy, can't use for nf %d TX\n", - cur_lcore, + if (rte_eal_remote_launch(tx_thread_main, (void *)tx_mgr, cur_lcore) == -EBUSY) { + RTE_LOG(ERR, APP, "Core %d is already busy, can't use for nf %d TX\n", cur_lcore, tx_mgr->tx_thread_info->first_nf); return -1; } @@ -318,15 +399,30 @@ main(int argc, char *argv[]) { rx_mgr->nf_rx_bufs = calloc(MAX_NFS, sizeof(struct packet_buf)); cur_lcore = rte_get_next_lcore(cur_lcore, 1, 1); if (rte_eal_remote_launch(rx_thread_main, (void *)rx_mgr, cur_lcore) == -EBUSY) { - RTE_LOG(ERR, - APP, - "Core %d is already busy, can't use for RX queue id %d\n", - cur_lcore, + RTE_LOG(ERR, APP, "Core %d is already busy, can't use for RX queue id %d\n", cur_lcore, rx_mgr->id); return -1; } } + if (ONVM_NF_SHARE_CORES) { + nfs_per_wakeup_thread = ceil((unsigned)MAX_NFS / wakeup_lcores); + for (i = 0; i < ONVM_NUM_WAKEUP_THREADS; i++) { + struct wakeup_thread_context *wakeup_ctx = calloc(1, sizeof(struct wakeup_thread_context)); + if (wakeup_ctx == NULL) { + RTE_LOG(ERR, APP, "Can't allocate wakeup info struct\n"); + return -1; + } + wakeup_ctx->first_nf = RTE_MIN(i * nfs_per_wakeup_thread + 1, (unsigned)MAX_NFS); + wakeup_ctx->last_nf = RTE_MIN((i + 1) * nfs_per_wakeup_thread + 1, (unsigned)MAX_NFS); + cur_lcore = rte_get_next_lcore(cur_lcore, 1, 1); + if (rte_eal_remote_launch(wakeup_thread_main, (void*)wakeup_ctx, cur_lcore) == -EBUSY) { + RTE_LOG(ERR, APP, "Core %d is already busy, can't use for nf %d wakeup thread\n", + cur_lcore, wakeup_ctx->first_nf); + return -1; + } + } + } /* Master thread handles statistics and NF management */ master_thread_main(); return 0; diff --git a/onvm/onvm_mgr/onvm_args.c b/onvm/onvm_mgr/onvm_args.c index 0e3547372..9989e9f77 100644 --- a/onvm/onvm_mgr/onvm_args.c +++ b/onvm/onvm_mgr/onvm_args.c @@ -5,9 +5,9 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside - * 2010-2014 Intel Corporation. All rights reserved. + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside + * 2010-2019 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,7 +38,6 @@ * ********************************************************************/ - /****************************************************************************** onvm_args.c @@ -48,14 +47,11 @@ ******************************************************************************/ - #include "onvm_mgr/onvm_args.h" #include "onvm_mgr/onvm_stats.h" - /******************************Global variables*******************************/ - /* global var for number of currently active NFs - extern in header init.h */ uint16_t num_nfs; @@ -71,28 +67,32 @@ ONVM_STATS_OUTPUT stats_destination = ONVM_STATS_NONE; /* global var for how long stats should wait before updating - extern in init.h */ uint16_t global_stats_sleep_time = 1; +/* global var for time_to_live, how long to wait until shutdown - extern in init.h */ +uint32_t global_time_to_live = 0; + +/* global var for time_to_live, how long to wait until shutdown - extern in init.h */ +uint32_t global_pkt_limit = 0; + /* global var for how verbose the stats output to console is - extern in init.h */ uint8_t global_verbosity_level = 1; +/* global flag for enabling shared core logic - extern in init.h */ +uint8_t ONVM_NF_SHARE_CORES = 0; + /* global var for program name */ static const char *progname; - /***********************Internal Functions prototypes*************************/ - static void usage(void); - static int parse_portmask(uint8_t max_ports, const char *portmask); - static int parse_default_service(const char *services); - static int parse_num_services(const char *services); @@ -102,6 +102,12 @@ parse_nf_cores(const char *services); static int parse_stats_output(const char *stats_output); +static int +parse_time_to_live(const char *time_to_live); + +static int +parse_packet_limit(const char *pkt_limit); + static int parse_stats_sleep_time(const char *sleeptime); @@ -110,25 +116,21 @@ parse_verbosity_level(const char *verbosity_level); /*********************************Interfaces**********************************/ - int parse_app_args(uint8_t max_ports, int argc, char *argv[]) { int option_index, opt; char **argvopt = argv; static struct option lgopts[] = { - {"port-mask", required_argument, NULL, 'p'}, - {"num-services", required_argument, NULL, 'r'}, - {"nf-cores", required_argument, NULL, 'n'}, - {"default-service", required_argument, NULL, 'd'}, - {"stats-out", no_argument, NULL, 's'}, - {"stats-sleep-time", no_argument, NULL, 'z'}, - {"verbocity-level", no_argument, NULL, 'v'} - }; + {"port-mask", required_argument, NULL, 'p'}, {"num-services", required_argument, NULL, 'r'}, + {"nf-cores", required_argument, NULL, 'n'}, {"default-service", required_argument, NULL, 'd'}, + {"stats-out", no_argument, NULL, 's'}, {"stats-sleep-time", no_argument, NULL, 'z'}, + {"time_to_live", no_argument, NULL, 't'}, {"packet_limit", no_argument, NULL, 'l'}, + {"verbocity-level", no_argument, NULL, 'v'}, {"enable_shared_cpu", no_argument, NULL, 'c'}}; progname = argv[0]; - while ((opt = getopt_long(argc, argvopt, "p:r:n:d:s:z:v:", lgopts, &option_index)) != EOF) { + while ((opt = getopt_long(argc, argvopt, "p:r:n:d:s:t:l:z:v:c", lgopts, &option_index)) != EOF) { switch (opt) { case 'p': if (parse_portmask(max_ports, optarg) != 0) { @@ -162,18 +164,34 @@ parse_app_args(uint8_t max_ports, int argc, char *argv[]) { onvm_stats_set_output(stats_destination); break; + case 't': + if (parse_time_to_live(optarg) != 0) { + usage(); + return -1; + } + break; + case 'l': + if (parse_packet_limit(optarg) != 0) { + usage(); + return -1; + } + break; case 'z': - if(parse_stats_sleep_time(optarg) != 0){ + if (parse_stats_sleep_time(optarg) != 0) { usage(); return -1; } break; case 'v': - if(parse_verbosity_level(optarg) != 0){ + if (parse_verbosity_level(optarg) != 0) { usage(); return -1; } break; + case 'c': + onvm_config->flags.ONVM_NF_SHARE_CORES = 1; + ONVM_NF_SHARE_CORES = 1; + break; default: printf("ERROR: Unknown option '%c'\n", opt); usage(); @@ -184,10 +202,8 @@ parse_app_args(uint8_t max_ports, int argc, char *argv[]) { return 0; } - /*****************************Internal functions******************************/ - static void usage(void) { printf( @@ -197,11 +213,13 @@ usage(void) { "\t-d DEFAULT_SERVICE: the service to initially receive packets. defaults to 1 (optional)\n" "\t-s STATS_OUTPUT: where to output manager stats (stdout/stderr/web). defaults to NONE (optional)\n" "\t-z STATS_SLEEP_TIME: how long the stats thread should wait before updating the stats (in seconds)\n" - "\t-v VERBOCITY_LEVEL: verbocity level of the stats output(optional)\n", + "\t-t TTL: time to live, how many seconds to wait until exiting (optional)\n" + "\t-l PACKET_LIMIT: how many millions of packets to recieve before exiting (optional)\n" + "\t-v VERBOCITY_LEVEL: verbocity level of the stats output (optional)\n" + "\t-c ENABLE_SHARED_CORE: allow the NFs to share a core based on mutex sleep/wakeups (optional)\n", progname); } - static int parse_portmask(uint8_t max_ports, const char *portmask) { char *end = NULL; @@ -224,10 +242,12 @@ parse_portmask(uint8_t max_ports, const char *portmask) { while (pm != 0) { if (pm & 0x01) { /* bit is set in mask, use port */ if (count >= max_ports) - printf("WARNING: requested port %u not present" - " - ignoring\n", (unsigned)count); + printf( + "WARNING: requested port %u not present" + " - ignoring\n", + (unsigned)count); else - ports->id[ports->num_ports++] = count; + ports->id[ports->num_ports++] = count; } pm = (pm >> 1); count++; @@ -236,7 +256,6 @@ parse_portmask(uint8_t max_ports, const char *portmask) { return 0; } - static int parse_default_service(const char *services) { char *end = NULL; @@ -250,7 +269,6 @@ parse_default_service(const char *services) { return 0; } - static int parse_num_services(const char *services) { char *end = NULL; @@ -290,8 +308,10 @@ parse_nf_cores(const char *nf_coremask) { while (pm != 0) { if (pm & 0x01) { /* bit is set in mask, use port */ if (count >= max_cores) { - printf("WARNING: requested core %u out of cpu bounds" - " - ignoring\n", (unsigned)count); + printf( + "WARNING: requested core %u out of cpu bounds" + " - ignoring\n", + (unsigned)count); } else { cores[count].enabled = 1; cores[count].nf_count = 0; @@ -321,11 +341,11 @@ parse_nf_cores(const char *nf_coremask) { static int parse_stats_sleep_time(const char *sleeptime) { - char* end = NULL; + char *end = NULL; unsigned long temp; temp = strtoul(sleeptime, &end, 10); - if(end == NULL || *end != '\0' || temp == 0) + if (end == NULL || *end != '\0' || temp == 0) return -1; global_stats_sleep_time = (uint16_t)temp; @@ -347,14 +367,40 @@ parse_stats_output(const char *stats_output) { return -1; } } - + +static int +parse_time_to_live(const char *time_to_live) { + char* end = NULL; + unsigned long temp; + + temp = strtoul(time_to_live, &end, 10); + if (end == NULL || *end != '\0' || temp == 0) + return -1; + + global_time_to_live = (uint32_t)temp; + return 0; +} + static int -parse_verbosity_level(const char *verbosity_level){ +parse_packet_limit(const char *pkt_limit) { char* end = NULL; unsigned long temp; + temp = strtoul(pkt_limit, &end, 10); + if (end == NULL || *end != '\0' || temp == 0) + return -1; + + global_pkt_limit = (uint32_t)temp; + return 0; +} + +static int +parse_verbosity_level(const char *verbosity_level) { + char *end = NULL; + unsigned long temp; + temp = strtoul(verbosity_level, &end, 10); - if(end == NULL || *end != '\0' || temp == 0) + if (end == NULL || *end != '\0' || temp == 0) return -1; global_verbosity_level = (uint16_t)temp; diff --git a/onvm/onvm_mgr/onvm_args.h b/onvm/onvm_mgr/onvm_args.h index 12db94009..4aa519c84 100644 --- a/onvm/onvm_mgr/onvm_args.h +++ b/onvm/onvm_mgr/onvm_args.h @@ -5,9 +5,9 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside - * 2010-2014 Intel Corporation. All rights reserved. + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside + * 2010-2019 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,7 +38,6 @@ * ********************************************************************/ - /****************************************************************************** onvm_args.h @@ -48,7 +47,6 @@ ******************************************************************************/ - #ifndef _ONVM_ARGS_H_ #define _ONVM_ARGS_H_ @@ -59,6 +57,7 @@ #define DEFAULT_SERVICE_ID 1 -int parse_app_args(uint8_t max_ports, int argc, char *argv[]); +int +parse_app_args(uint8_t max_ports, int argc, char *argv[]); #endif // _ONVM_ARGS_H_ diff --git a/onvm/onvm_mgr/onvm_init.c b/onvm/onvm_mgr/onvm_init.c index 0e0c11e46..e17bc8ae1 100644 --- a/onvm/onvm_mgr/onvm_init.c +++ b/onvm/onvm_mgr/onvm_init.c @@ -5,9 +5,9 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside - * 2010-2014 Intel Corporation. All rights reserved. + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside + * 2010-2019 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,7 +38,6 @@ * ********************************************************************/ - /****************************************************************************** onvm_init.c @@ -48,19 +47,18 @@ ******************************************************************************/ - #include "onvm_mgr/onvm_init.h" - /********************************Global variables*****************************/ - struct onvm_nf *nfs = NULL; struct port_info *ports = NULL; struct core_status *cores = NULL; +struct onvm_configuration *onvm_config = NULL; +struct nf_wakeup_info *nf_wakeup_infos = NULL; struct rte_mempool *pktmbuf_pool; -struct rte_mempool *nf_info_pool; +struct rte_mempool *nf_init_cfg_pool; struct rte_mempool *nf_msg_pool; struct rte_ring *incoming_msg_queue; uint16_t **services; @@ -68,17 +66,34 @@ uint16_t *nf_per_service_count; struct onvm_service_chain *default_chain; struct onvm_service_chain **default_sc_p; - /*************************Internal Functions Prototypes***********************/ -static int init_mbuf_pools(void); -static int init_nf_info_pool(void); -static int init_nf_msg_pool(void); -static int init_port(uint8_t port_num); -static int init_shm_rings(void); -static int init_info_queue(void); -static void check_all_ports_link_status(uint8_t port_num, uint32_t port_mask); +static void +set_default_config(struct onvm_configuration *config); + +static int +init_mbuf_pools(void); + +static int +init_nf_init_cfg_pool(void); + +static int +init_nf_msg_pool(void); + +static int +init_port(uint8_t port_num); + +static int +init_shm_rings(void); +static void +init_shared_sem(void); + +static int +init_info_queue(void); + +static void +check_all_ports_link_status(uint8_t port_num, uint32_t port_mask); /*****************Internal Configuration Structs and Constants*****************/ @@ -102,30 +117,23 @@ static void check_all_ports_link_status(uint8_t port_num, uint32_t port_mask); #define TX_WTHRESH 0 /* Default values of TX write-back threshold reg. */ static const struct rte_eth_conf port_conf = { - .rxmode = { - .mq_mode = ETH_MQ_RX_RSS, - .max_rx_pkt_len = ETHER_MAX_LEN, - .split_hdr_size = 0, - .offloads = DEV_RX_OFFLOAD_CHECKSUM, + .rxmode = { + .mq_mode = ETH_MQ_RX_RSS, + .max_rx_pkt_len = ETHER_MAX_LEN, + .split_hdr_size = 0, + .offloads = DEV_RX_OFFLOAD_CHECKSUM, }, - .rx_adv_conf = { - .rss_conf = { - .rss_key = rss_symmetric_key, - .rss_hf = ETH_RSS_IP | ETH_RSS_UDP | ETH_RSS_TCP | ETH_RSS_L2_PAYLOAD, + .rx_adv_conf = { + .rss_conf = { + .rss_key = rss_symmetric_key, .rss_hf = ETH_RSS_IP | ETH_RSS_UDP | ETH_RSS_TCP | ETH_RSS_L2_PAYLOAD, }, }, - .txmode = { - .mq_mode = ETH_MQ_TX_NONE, - .offloads = (DEV_TX_OFFLOAD_IPV4_CKSUM | - DEV_TX_OFFLOAD_UDP_CKSUM | - DEV_TX_OFFLOAD_TCP_CKSUM) - }, + .txmode = {.mq_mode = ETH_MQ_TX_NONE, + .offloads = (DEV_TX_OFFLOAD_IPV4_CKSUM | DEV_TX_OFFLOAD_UDP_CKSUM | DEV_TX_OFFLOAD_TCP_CKSUM)}, }; - /*********************************Interfaces**********************************/ - int init(int argc, char *argv[]) { int retval; @@ -135,6 +143,7 @@ init(int argc, char *argv[]) { const struct rte_memzone *mz_scp; const struct rte_memzone *mz_services; const struct rte_memzone *mz_nf_per_service; + const struct rte_memzone *mz_onvm_config; uint8_t i, total_ports, port_id; /* init EAL, parsing EAL args */ @@ -152,43 +161,50 @@ init(int argc, char *argv[]) { total_ports = rte_eth_dev_count_avail(); /* set up array for NF tx data */ - mz_nf = rte_memzone_reserve(MZ_NF_INFO, sizeof(*nfs) * MAX_NFS, - rte_socket_id(), NO_FLAGS); + mz_nf = rte_memzone_reserve(MZ_NF_INFO, sizeof(*nfs) * MAX_NFS, rte_socket_id(), NO_FLAGS); if (mz_nf == NULL) rte_exit(EXIT_FAILURE, "Cannot reserve memory zone for nf information\n"); memset(mz_nf->addr, 0, sizeof(*nfs) * MAX_NFS); nfs = mz_nf->addr; /* set up ports info */ - mz_port = rte_memzone_reserve(MZ_PORT_INFO, sizeof(*ports), - rte_socket_id(), NO_FLAGS); + mz_port = rte_memzone_reserve(MZ_PORT_INFO, sizeof(*ports), rte_socket_id(), NO_FLAGS); if (mz_port == NULL) rte_exit(EXIT_FAILURE, "Cannot reserve memory zone for port information\n"); ports = mz_port->addr; - + /* set up core status */ mz_cores = rte_memzone_reserve(MZ_CORES_STATUS, sizeof(*cores) * onvm_threading_get_num_cores(), - rte_socket_id(), NO_FLAGS); + rte_socket_id(), NO_FLAGS); if (mz_cores == NULL) rte_exit(EXIT_FAILURE, "Cannot reserve memory zone for core information\n"); memset(mz_cores->addr, 0, sizeof(*cores) * 64); cores = mz_cores->addr; /* set up array for NF tx data */ - mz_services = rte_memzone_reserve(MZ_SERVICES_INFO, sizeof(uint16_t *) * num_services, rte_socket_id(), NO_FLAGS); + mz_services = + rte_memzone_reserve(MZ_SERVICES_INFO, sizeof(uint16_t *) * num_services, rte_socket_id(), NO_FLAGS); if (mz_services == NULL) rte_exit(EXIT_FAILURE, "Cannot reserve memory zone for services information\n"); services = mz_services->addr; for (i = 0; i < num_services; i++) { - services[i] = rte_calloc("one service NFs", - MAX_NFS_PER_SERVICE, sizeof(uint16_t), 0); + services[i] = rte_calloc("one service NFs", MAX_NFS_PER_SERVICE, sizeof(uint16_t), 0); } - mz_nf_per_service = rte_memzone_reserve(MZ_NF_PER_SERVICE_INFO, sizeof(uint16_t) * num_services, rte_socket_id(), NO_FLAGS); + mz_nf_per_service = + rte_memzone_reserve(MZ_NF_PER_SERVICE_INFO, sizeof(uint16_t) * num_services, rte_socket_id(), NO_FLAGS); if (mz_nf_per_service == NULL) { rte_exit(EXIT_FAILURE, "Cannot reserve memory zone for NF per service information.\n"); } nf_per_service_count = mz_nf_per_service->addr; + /* set up custom flags */ + mz_onvm_config = rte_memzone_reserve(MZ_ONVM_CONFIG, sizeof(uint16_t), rte_socket_id(), NO_FLAGS); + if (mz_onvm_config == NULL) { + rte_exit(EXIT_FAILURE, "Cannot reserve memory zone for ONVM custom flags.\n"); + } + onvm_config = mz_onvm_config->addr; + set_default_config(onvm_config); + /* parse additional, application arguments */ retval = parse_app_args(total_ports, argc, argv); if (retval != 0) @@ -200,7 +216,7 @@ init(int argc, char *argv[]) { rte_exit(EXIT_FAILURE, "Cannot create needed mbuf pools\n"); /* initialise nf info pool */ - retval = init_nf_info_pool(); + retval = init_nf_init_cfg_pool(); if (retval != 0) { rte_exit(EXIT_FAILURE, "Cannot create nf info mbuf pool: %s\n", rte_strerror(rte_errno)); } @@ -218,9 +234,9 @@ init(int argc, char *argv[]) { retval = init_port(port_id); if (retval != 0) rte_exit(EXIT_FAILURE, "Cannot initialise port %u\n", port_id); - char event_msg_buf[20]; - sprintf(event_msg_buf, "Port %d initialized", port_id); - onvm_stats_add_event(event_msg_buf, NULL); + char event_msg_buf[22]; + snprintf(event_msg_buf, sizeof(event_msg_buf), "Port %d initialized", port_id); + onvm_stats_gen_event_info(event_msg_buf, ONVM_EVENT_PORT_INFO, NULL); } check_all_ports_link_status(ports->num_ports, (~0x0)); @@ -231,20 +247,22 @@ init(int argc, char *argv[]) { /* initialise a queue for newly created NFs */ init_info_queue(); + /* initialise the shared memory for shared core mode */ + init_shared_sem(); + /*initialize a default service chain*/ default_chain = onvm_sc_create(); retval = onvm_sc_append_entry(default_chain, ONVM_NF_ACTION_TONF, 1); if (retval == ENOSPC) { - printf("chain length can not be larger than the maximum chain length\n"); + printf("Chain length can not be larger than the maximum chain length\n"); exit(1); } printf("Default service chain: send to sdn NF\n"); /* set up service chain pointer shared to NFs*/ - mz_scp = rte_memzone_reserve(MZ_SCP_INFO, sizeof(struct onvm_service_chain *), - rte_socket_id(), NO_FLAGS); + mz_scp = rte_memzone_reserve(MZ_SCP_INFO, sizeof(struct onvm_service_chain *), rte_socket_id(), NO_FLAGS); if (mz_scp == NULL) - rte_exit(EXIT_FAILURE, "Canot reserve memory zone for service chain pointer\n"); + rte_exit(EXIT_FAILURE, "Cannot reserve memory zone for service chain pointer\n"); memset(mz_scp->addr, 0, sizeof(struct onvm_service_chain *)); default_sc_p = mz_scp->addr; *default_sc_p = default_chain; @@ -255,9 +273,15 @@ init(int argc, char *argv[]) { return 0; } - /*****************************Internal functions******************************/ +/** + * Initialise the default onvm config structure + */ +static void +set_default_config(struct onvm_configuration *config) { + config->flags.ONVM_NF_SHARE_CORES = ONVM_NF_SHARE_CORES_DEFAULT; +} /** * Initialise the mbuf pool for packet reception for the NIC, and any other @@ -267,12 +291,10 @@ static int init_mbuf_pools(void) { /* don't pass single-producer/single-consumer flags to mbuf create as it * seems faster to use a cache instead */ - printf("Creating mbuf pool '%s' [%u mbufs] ...\n", - PKTMBUF_POOL_NAME, NUM_MBUFS); - pktmbuf_pool = rte_mempool_create(PKTMBUF_POOL_NAME, NUM_MBUFS, - MBUF_SIZE, MBUF_CACHE_SIZE, - sizeof(struct rte_pktmbuf_pool_private), rte_pktmbuf_pool_init, - NULL, rte_pktmbuf_init, NULL, rte_socket_id(), NO_FLAGS); + printf("Creating mbuf pool '%s' [%u mbufs] ...\n", PKTMBUF_POOL_NAME, NUM_MBUFS); + pktmbuf_pool = rte_mempool_create(PKTMBUF_POOL_NAME, NUM_MBUFS, MBUF_SIZE, MBUF_CACHE_SIZE, + sizeof(struct rte_pktmbuf_pool_private), rte_pktmbuf_pool_init, NULL, + rte_pktmbuf_init, NULL, rte_socket_id(), NO_FLAGS); return (pktmbuf_pool == NULL); /* 0 on success */ } @@ -281,32 +303,28 @@ init_mbuf_pools(void) { * Set up a mempool to store nf_msg structs */ static int -init_nf_msg_pool(void) -{ +init_nf_msg_pool(void) { /* don't pass single-producer/single-consumer flags to mbuf * create as it seems faster to use a cache instead */ printf("Creating mbuf pool '%s' ...\n", _NF_MSG_POOL_NAME); - nf_msg_pool = rte_mempool_create(_NF_MSG_POOL_NAME, MAX_NFS * NF_MSG_QUEUE_SIZE, - NF_INFO_SIZE, NF_MSG_CACHE_SIZE, - 0, NULL, NULL, NULL, NULL, rte_socket_id(), NO_FLAGS); + nf_msg_pool = rte_mempool_create(_NF_MSG_POOL_NAME, MAX_NFS * NF_MSG_QUEUE_SIZE, NF_INFO_SIZE, + NF_MSG_CACHE_SIZE, 0, NULL, NULL, NULL, NULL, rte_socket_id(), NO_FLAGS); return (nf_msg_pool == NULL); /* 0 on success */ } /** - * Set up a mempool to store nf_info structs + * Set up a mempool to store nf_init_cfg structs */ static int -init_nf_info_pool(void) -{ +init_nf_init_cfg_pool(void) { /* don't pass single-producer/single-consumer flags to mbuf * create as it seems faster to use a cache instead */ printf("Creating mbuf pool '%s' ...\n", _NF_MEMPOOL_NAME); - nf_info_pool = rte_mempool_create(_NF_MEMPOOL_NAME, MAX_NFS, - NF_INFO_SIZE, 0, - 0, NULL, NULL, NULL, NULL, rte_socket_id(), NO_FLAGS); + nf_init_cfg_pool = rte_mempool_create(_NF_MEMPOOL_NAME, MAX_NFS, NF_INFO_SIZE, 0, 0, NULL, NULL, NULL, NULL, + rte_socket_id(), NO_FLAGS); - return (nf_info_pool == NULL); /* 0 on success */ + return (nf_init_cfg_pool == NULL); /* 0 on success */ } /** @@ -343,58 +361,96 @@ init_port(uint8_t port_num) { rte_eth_dev_info_get(port_num, &dev_info); if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE) local_port_conf.txmode.offloads |= DEV_TX_OFFLOAD_MBUF_FAST_FREE; - local_port_conf.rx_adv_conf.rss_conf.rss_hf &= - dev_info.flow_type_rss_offloads; - if (local_port_conf.rx_adv_conf.rss_conf.rss_hf != - port_conf.rx_adv_conf.rss_conf.rss_hf) { - printf("Port %u modified RSS hash function based on hardware support," - "requested:%#"PRIx64" configured:%#"PRIx64"\n", - port_num, - port_conf.rx_adv_conf.rss_conf.rss_hf, - local_port_conf.rx_adv_conf.rss_conf.rss_hf); + local_port_conf.rx_adv_conf.rss_conf.rss_hf &= dev_info.flow_type_rss_offloads; + if (local_port_conf.rx_adv_conf.rss_conf.rss_hf != port_conf.rx_adv_conf.rss_conf.rss_hf) { + printf( + "Port %u modified RSS hash function based on hardware support," + "requested:%#" PRIx64 " configured:%#" PRIx64 "\n", + port_num, port_conf.rx_adv_conf.rss_conf.rss_hf, local_port_conf.rx_adv_conf.rss_conf.rss_hf); } - if ((retval = rte_eth_dev_configure(port_num, rx_rings, tx_rings, - &local_port_conf)) != 0) + if ((retval = rte_eth_dev_configure(port_num, rx_rings, tx_rings, &local_port_conf)) != 0) return retval; - /* Adjust rx,tx ring sizes if not allowed by ethernet device + /* Adjust rx,tx ring sizes if not allowed by ethernet device * TODO if this is ajusted store the new values for future reference */ - retval = rte_eth_dev_adjust_nb_rx_tx_desc( - port_num, &rx_ring_size, &tx_ring_size); + retval = rte_eth_dev_adjust_nb_rx_tx_desc(port_num, &rx_ring_size, &tx_ring_size); if (retval < 0) { - rte_panic("Cannot adjust number of descriptors for port %u (%d)\n", - port_num, retval); + rte_panic("Cannot adjust number of descriptors for port %u (%d)\n", port_num, retval); } rxq_conf = dev_info.default_rxconf; rxq_conf.offloads = local_port_conf.rxmode.offloads; for (q = 0; q < rx_rings; q++) { - retval = rte_eth_rx_queue_setup(port_num, q, rx_ring_size, - rte_eth_dev_socket_id(port_num), - &rxq_conf, pktmbuf_pool); - if (retval < 0) return retval; + retval = rte_eth_rx_queue_setup(port_num, q, rx_ring_size, rte_eth_dev_socket_id(port_num), &rxq_conf, + pktmbuf_pool); + if (retval < 0) + return retval; } txq_conf = dev_info.default_txconf; txq_conf.offloads = port_conf.txmode.offloads; for (q = 0; q < tx_rings; q++) { - retval = rte_eth_tx_queue_setup(port_num, q, tx_ring_size, - rte_eth_dev_socket_id(port_num), - &txq_conf); - if (retval < 0) return retval; + retval = rte_eth_tx_queue_setup(port_num, q, tx_ring_size, rte_eth_dev_socket_id(port_num), &txq_conf); + if (retval < 0) + return retval; } rte_eth_promiscuous_enable(port_num); retval = rte_eth_dev_start(port_num); - if (retval < 0) return retval; + if (retval < 0) + return retval; printf("done: \n"); return 0; } +/** + * Initialize shared core structs (mutex/semaphore) + */ +static void +init_shared_sem(void) { + uint16_t i; + key_t key; + int shmid; + char *shm; + sem_t *mutex; + const char * sem_name; + + nf_wakeup_infos = rte_calloc("MGR_SHM_INFOS", sizeof(struct nf_wakeup_info), MAX_NFS, 0); + + if (!ONVM_NF_SHARE_CORES) + return; + + for (i = 0; i < MAX_NFS; i++) { + sem_name = get_sem_name(i); + nf_wakeup_infos[i].sem_name = sem_name; + + mutex = sem_open(sem_name, O_CREAT, 06666, 0); + if (mutex == SEM_FAILED) { + fprintf(stderr, "can not create semaphore for NF %d\n", i); + sem_unlink(sem_name); + exit(1); + } + nf_wakeup_infos[i].mutex = mutex; + + key = get_rx_shmkey(i); + if ((shmid = shmget(key, SHMSZ, IPC_CREAT | 0666)) < 0) { + fprintf(stderr, "can not create the shared memory segment for NF %d\n", i); + exit(1); + } + + if ((shm = shmat(shmid, NULL, 0)) == (char *) -1) { + fprintf(stderr, "can not attach the shared segment to the server space for NF %d\n", i); + exit(1); + } + + nf_wakeup_infos[i].shm_server = (rte_atomic16_t *)shm; + } +} + /** * Set up the DPDK rings which will be used to pass packets, via * pointers, between the multi-process server and NF processes. @@ -404,9 +460,9 @@ static int init_shm_rings(void) { unsigned i; unsigned socket_id; - const char * rq_name; - const char * tq_name; - const char * msg_q_name; + const char *rq_name; + const char *tq_name; + const char *msg_q_name; const unsigned ringsize = NF_QUEUE_RINGSIZE; const unsigned msgringsize = NF_MSG_QUEUE_SIZE; @@ -420,15 +476,12 @@ init_shm_rings(void) { tq_name = get_tx_queue_name(i); msg_q_name = get_msg_queue_name(i); nfs[i].instance_id = i; - nfs[i].rx_q = rte_ring_create(rq_name, - ringsize, socket_id, - RING_F_SC_DEQ); /* multi prod, single cons */ - nfs[i].tx_q = rte_ring_create(tq_name, - ringsize, socket_id, - RING_F_SC_DEQ); /* multi prod, single cons */ - nfs[i].msg_q = rte_ring_create(msg_q_name, - msgringsize, socket_id, - RING_F_SC_DEQ); /* multi prod, single cons */ + nfs[i].rx_q = + rte_ring_create(rq_name, ringsize, socket_id, RING_F_SC_DEQ); /* multi prod, single cons */ + nfs[i].tx_q = + rte_ring_create(tq_name, ringsize, socket_id, RING_F_SC_DEQ); /* multi prod, single cons */ + nfs[i].msg_q = + rte_ring_create(msg_q_name, msgringsize, socket_id, RING_F_SC_DEQ); /* multi prod, single cons */ if (nfs[i].rx_q == NULL) rte_exit(EXIT_FAILURE, "Cannot create rx ring queue for NF %u\n", i); @@ -446,13 +499,9 @@ init_shm_rings(void) { * Allocate a rte_ring for newly created NFs */ static int -init_info_queue(void) -{ - incoming_msg_queue = rte_ring_create( - _MGR_MSG_QUEUE_NAME, - MAX_NFS, - rte_socket_id(), - RING_F_SC_DEQ); // MP enqueue (default), SC dequeue +init_info_queue(void) { + incoming_msg_queue = rte_ring_create(_MGR_MSG_QUEUE_NAME, MAX_NFS, rte_socket_id(), + RING_F_SC_DEQ); // MP enqueue (default), SC dequeue if (incoming_msg_queue == NULL) rte_exit(EXIT_FAILURE, "Cannot create incoming msg queue\n"); @@ -464,7 +513,7 @@ init_info_queue(void) static void check_all_ports_link_status(uint8_t port_num, uint32_t port_mask) { #define CHECK_INTERVAL 100 /* 100ms */ -#define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */ +#define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */ uint8_t portid, count, all_ports_up, print_flag = 0; struct rte_eth_link link; @@ -480,14 +529,14 @@ check_all_ports_link_status(uint8_t port_num, uint32_t port_mask) { /* print link status if flag set */ if (print_flag == 1) { if (link.link_status) - printf("Port %d Link Up - speed %u " - "Mbps - %s\n", ports->id[portid], - (unsigned)link.link_speed, - (link.link_duplex == ETH_LINK_FULL_DUPLEX) ? - ("full-duplex") : ("half-duplex\n")); + printf( + "Port %d Link Up - speed %u " + "Mbps - %s\n", + ports->id[portid], (unsigned)link.link_speed, + (link.link_duplex == ETH_LINK_FULL_DUPLEX) ? ("full-duplex") + : ("half-duplex\n")); else - printf("Port %d Link Down\n", - (uint8_t)ports->id[portid]); + printf("Port %d Link Down\n", (uint8_t)ports->id[portid]); continue; } /* clear all_ports_up flag if any link down */ diff --git a/onvm/onvm_mgr/onvm_init.h b/onvm/onvm_mgr/onvm_init.h index 24062b9ab..f133038e4 100644 --- a/onvm/onvm_mgr/onvm_init.h +++ b/onvm/onvm_mgr/onvm_init.h @@ -5,9 +5,9 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside - * 2010-2014 Intel Corporation. All rights reserved. + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside + * 2010-2019 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,7 +38,6 @@ * ********************************************************************/ - /****************************************************************************** onvm_init.h @@ -49,46 +48,41 @@ ******************************************************************************/ - #ifndef _ONVM_INIT_H_ #define _ONVM_INIT_H_ - /********************************DPDK library*********************************/ #include -#include -#include -#include #include #include +#include +#include +#include #ifdef RTE_LIBRTE_PDUMP #include #endif - /*****************************Internal library********************************/ - +#include "onvm_common.h" +#include "onvm_flow_dir.h" +#include "onvm_flow_table.h" +#include "onvm_includes.h" #include "onvm_mgr/onvm_args.h" #include "onvm_mgr/onvm_stats.h" -#include "onvm_includes.h" -#include "onvm_common.h" -#include "onvm_sc_mgr.h" #include "onvm_sc_common.h" -#include "onvm_flow_table.h" -#include "onvm_flow_dir.h" +#include "onvm_sc_mgr.h" #include "onvm_threading.h" /***********************************Macros************************************/ - #define MBUF_CACHE_SIZE 512 #define MBUF_OVERHEAD (sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM) #define RX_MBUF_DATA_SIZE 2048 #define MBUF_SIZE (RX_MBUF_DATA_SIZE + MBUF_OVERHEAD) -#define NF_INFO_SIZE sizeof(struct onvm_nf_info) +#define NF_INFO_SIZE sizeof(struct onvm_nf_init_cfg) #define NF_MSG_SIZE sizeof(struct onvm_nf_msg) #define NF_MSG_CACHE_SIZE 8 @@ -102,11 +96,10 @@ #define ONVM_NUM_RX_THREADS 1 /* Number of auxiliary threads in manager, 1 reserved for stats */ #define ONVM_NUM_MGR_AUX_THREADS 1 - +#define ONVM_NUM_WAKEUP_THREADS 1 // Enabled when using shared core mode /*************************External global variables***************************/ - /* NF to Manager data flow */ extern struct rte_ring *incoming_msg_queue; @@ -126,8 +119,17 @@ extern struct onvm_service_chain *default_chain; extern struct onvm_ft *sdn_ft; extern ONVM_STATS_OUTPUT stats_destination; extern uint16_t global_stats_sleep_time; +extern uint32_t global_time_to_live; +extern uint32_t global_pkt_limit; extern uint8_t global_verbosity_level; +/* Custom flags for onvm */ +extern struct onvm_configuration *onvm_config; +extern uint8_t ONVM_NF_SHARE_CORES; + +/* For handling shared core logic */ +extern struct nf_wakeup_info *nf_wakeup_infos; + /**********************************Functions**********************************/ /* @@ -139,6 +141,7 @@ extern uint8_t global_verbosity_level; * Output : an error code * */ -int init(int argc, char *argv[]); +int +init(int argc, char *argv[]); #endif // _ONVM_INIT_H_ diff --git a/onvm/onvm_mgr/onvm_mgr.h b/onvm/onvm_mgr/onvm_mgr.h index fb6d8c78c..4503ef1f7 100644 --- a/onvm/onvm_mgr/onvm_mgr.h +++ b/onvm/onvm_mgr/onvm_mgr.h @@ -5,9 +5,9 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside - * 2010-2014 Intel Corporation. All rights reserved. + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside + * 2010-2019 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,7 +38,6 @@ * ********************************************************************/ - /****************************************************************************** onvm_mgr.h @@ -49,51 +48,35 @@ ******************************************************************************/ - #ifndef _ONVM_MGR_H_ #define _ONVM_MGR_H_ - /******************************Standard C library*****************************/ - +#include #include #include -#include - /********************************DPDK library*********************************/ - #include -#include -#include #include - +#include +#include /******************************Internal headers*******************************/ - +#include "onvm_flow_dir.h" +#include "onvm_flow_table.h" +#include "onvm_includes.h" #include "onvm_mgr/onvm_args.h" #include "onvm_mgr/onvm_init.h" -#include "onvm_includes.h" -#include "onvm_sc_mgr.h" -#include "onvm_flow_table.h" -#include "onvm_flow_dir.h" #include "onvm_pkt_common.h" - +#include "onvm_sc_mgr.h" /***********************************Macros************************************/ - #define TO_PORT 0 #define TO_NF 1 - -/***************************Shared global variables***************************/ - - -/* ID to be assigned to the next NF that starts */ -extern uint16_t next_instance_id; - #endif // _ONVM_MGR_H_ diff --git a/onvm/onvm_mgr/onvm_nf.c b/onvm/onvm_mgr/onvm_nf.c index 00b3e36bb..d0a797a73 100644 --- a/onvm/onvm_mgr/onvm_nf.c +++ b/onvm/onvm_mgr/onvm_nf.c @@ -5,9 +5,9 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside - * 2010-2014 Intel Corporation. All rights reserved. + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside + * 2010-2019 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,7 +38,6 @@ * ********************************************************************/ - /****************************************************************************** onvm_nf.c @@ -47,16 +46,17 @@ ******************************************************************************/ - -#include "onvm_mgr.h" #include "onvm_nf.h" +#include "onvm_mgr.h" #include "onvm_stats.h" +#include -uint16_t next_instance_id = 0; +/* ID 0 is reserved */ +uint16_t next_instance_id = 1; +uint16_t starting_instance_id = 1; /************************Internal functions prototypes************************/ - /* * Function starting a NF. * @@ -65,8 +65,7 @@ uint16_t next_instance_id = 0; * */ inline static int -onvm_nf_start(struct onvm_nf_info *nf_info); - +onvm_nf_start(struct onvm_nf_init_cfg *nf_init_cfg); /* * Function to mark a NF as ready. @@ -76,8 +75,7 @@ onvm_nf_start(struct onvm_nf_info *nf_info); * */ inline static int -onvm_nf_ready(struct onvm_nf_info *nf_info); - +onvm_nf_ready(struct onvm_nf *nf); /* * Function stopping a NF. @@ -87,77 +85,115 @@ onvm_nf_ready(struct onvm_nf_info *nf_info); * */ inline static int -onvm_nf_stop(struct onvm_nf_info *nf_info); +onvm_nf_stop(struct onvm_nf *nf); +/* + * Function that initializes an LPM object + * + * Input : the address of an lpm_request struct + * Output : a return code based on initialization of the LPM object + * + */ +static void +onvm_nf_init_lpm_region(struct lpm_request *req_lpm); /********************************Interfaces***********************************/ - uint16_t onvm_nf_next_instance_id(void) { struct onvm_nf *nf; - uint16_t instance_id = MAX_NFS; + uint16_t instance_id; + if (num_nfs >= MAX_NFS) + return MAX_NFS; + + /* Do a first pass for NF IDs bigger than current next_instance_id */ while (next_instance_id < MAX_NFS) { instance_id = next_instance_id++; + /* Check if this id is occupied by another NF */ nf = &nfs[instance_id]; if (!onvm_nf_is_valid(nf)) - break; + return instance_id; } - return instance_id; -} + /* Reset to starting position */ + next_instance_id = starting_instance_id; + + /* Do a second pass for other NF IDs */ + while (next_instance_id < MAX_NFS) { + instance_id = next_instance_id++; + /* Check if this id is occupied by another NF */ + nf = &nfs[instance_id]; + if (!onvm_nf_is_valid(nf)) + return instance_id; + } + /* This should never happen, means our num_nfs counter is wrong */ + RTE_LOG(ERR, APP, "Tried to allocated a next instance ID but num_nfs is corrupted\n"); + return MAX_NFS; +} void onvm_nf_check_status(void) { int i; void *msgs[MAX_NFS]; + struct onvm_nf *nf; struct onvm_nf_msg *msg; - struct onvm_nf_info *nf; + struct onvm_nf_init_cfg *nf_init_cfg; + struct lpm_request *req_lpm; + uint16_t stop_nf_id; int num_msgs = rte_ring_count(incoming_msg_queue); - if (num_msgs == 0) return; + if (num_msgs == 0) + return; if (rte_ring_dequeue_bulk(incoming_msg_queue, msgs, num_msgs, NULL) == 0) return; for (i = 0; i < num_msgs; i++) { - msg = (struct onvm_nf_msg*) msgs[i]; + msg = (struct onvm_nf_msg *)msgs[i]; switch (msg->msg_type) { - case MSG_NF_STARTING: - nf = (struct onvm_nf_info*)msg->msg_data; - if (onvm_nf_start(nf) == 0) { - onvm_stats_add_event("NF Starting", nf); - } - break; - case MSG_NF_READY: - nf = (struct onvm_nf_info*)msg->msg_data; - if (onvm_nf_ready(nf) == 0) { - onvm_stats_add_event("NF Ready", nf); - } - break; - case MSG_NF_STOPPING: - nf = (struct onvm_nf_info*)msg->msg_data; - if (onvm_nf_stop(nf) == 0) { - onvm_stats_add_event("NF Stopping", nf); - num_nfs--; - } - break; + case MSG_REQUEST_LPM_REGION: + // TODO: Add stats event handler here + req_lpm = (struct lpm_request *)msg->msg_data; + onvm_nf_init_lpm_region(req_lpm); + break; + case MSG_NF_STARTING: + nf_init_cfg = (struct onvm_nf_init_cfg *)msg->msg_data; + if (onvm_nf_start(nf_init_cfg) == 0) { + onvm_stats_gen_event_nf_info("NF Starting", &nfs[nf_init_cfg->instance_id]); + } + break; + case MSG_NF_READY: + nf = (struct onvm_nf *)msg->msg_data; + if (onvm_nf_ready(nf) == 0) { + onvm_stats_gen_event_nf_info("NF Ready", nf); + } + break; + case MSG_NF_STOPPING: + nf = (struct onvm_nf *)msg->msg_data; + if (nf == NULL) + break; + + /* Saved as onvm_nf_stop frees the memory */ + stop_nf_id = nf->instance_id; + if (onvm_nf_stop(nf) == 0) { + onvm_stats_gen_event_info("NF Stopping", ONVM_EVENT_NF_STOP, &stop_nf_id); + } + break; } - rte_mempool_put(nf_msg_pool, (void*)msg); + rte_mempool_put(nf_msg_pool, (void *)msg); } } - int onvm_nf_send_msg(uint16_t dest, uint8_t msg_type, void *msg_data) { int ret; struct onvm_nf_msg *msg; - ret = rte_mempool_get(nf_msg_pool, (void**)(&msg)); + ret = rte_mempool_get(nf_msg_pool, (void **)(&msg)); if (ret != 0) { RTE_LOG(INFO, APP, "Oh the huge manatee! Unable to allocate msg from pool :(\n"); return ret; @@ -166,103 +202,155 @@ onvm_nf_send_msg(uint16_t dest, uint8_t msg_type, void *msg_data) { msg->msg_type = msg_type; msg->msg_data = msg_data; - return rte_ring_sp_enqueue(nfs[dest].msg_q, (void*)msg); + return rte_ring_enqueue(nfs[dest].msg_q, (void *)msg); } /******************************Internal functions*****************************/ - inline static int -onvm_nf_start(struct onvm_nf_info *nf_info) { +onvm_nf_start(struct onvm_nf_init_cfg *nf_init_cfg) { + struct onvm_nf *spawned_nf; + uint16_t nf_id; int ret; // TODO dynamically allocate memory here - make rx/tx ring // take code from init_shm_rings in init.c // flush rx/tx queue at the this index to start clean? - if(nf_info == NULL || nf_info->status != NF_WAITING_FOR_ID) + if (nf_init_cfg == NULL || nf_init_cfg->status != NF_WAITING_FOR_ID) return 1; // if NF passed its own id on the command line, don't assign here // assume user is smart enough to avoid duplicates - uint16_t nf_id = nf_info->instance_id == (uint16_t)NF_NO_ID - ? onvm_nf_next_instance_id() - : nf_info->instance_id; + nf_id = nf_init_cfg->instance_id == (uint16_t)NF_NO_ID ? onvm_nf_next_instance_id() : nf_init_cfg->instance_id; + spawned_nf = &nfs[nf_id]; if (nf_id >= MAX_NFS) { // There are no more available IDs for this NF - nf_info->status = NF_NO_IDS; + nf_init_cfg->status = NF_NO_IDS; return 1; } - if (nf_info->service_id >= MAX_SERVICES) { + if (nf_init_cfg->service_id >= MAX_SERVICES) { // Service ID must be less than MAX_SERVICES and greater than 0 - nf_info->status = NF_SERVICE_MAX; + nf_init_cfg->status = NF_SERVICE_MAX; return 1; } - if (nf_per_service_count[nf_info->service_id] >= MAX_NFS_PER_SERVICE) { + if (nf_per_service_count[nf_init_cfg->service_id] >= MAX_NFS_PER_SERVICE) { // Maximum amount of NF's per service spawned - nf_info->status = NF_SERVICE_COUNT_MAX; + nf_init_cfg->status = NF_SERVICE_COUNT_MAX; return 1; } - if (onvm_nf_is_valid(&nfs[nf_id])) { + if (onvm_nf_is_valid(spawned_nf)) { // This NF is trying to declare an ID already in use - nf_info->status = NF_ID_CONFLICT; + nf_init_cfg->status = NF_ID_CONFLICT; return 1; } // Keep reference to this NF in the manager - nf_info->instance_id = nf_id; + nf_init_cfg->instance_id = nf_id; /* If not successful return will contain the error code */ - ret = onvm_threading_get_core(&nf_info->core, nf_info->flags, cores); + ret = onvm_threading_get_core(&nf_init_cfg->core, nf_init_cfg->init_options, cores); if (ret != 0) { - nf_info->status = ret; + nf_init_cfg->status = ret; return 1; } - nfs[nf_id].info = nf_info; - nfs[nf_id].instance_id = nf_id; - + spawned_nf->instance_id = nf_id; + spawned_nf->service_id = nf_init_cfg->service_id; + spawned_nf->status = NF_STARTING; + spawned_nf->tag = nf_init_cfg->tag; + spawned_nf->thread_info.core = nf_init_cfg->core; + spawned_nf->flags.time_to_live = nf_init_cfg->time_to_live; + spawned_nf->flags.pkt_limit = nf_init_cfg->pkt_limit; // Let the NF continue its init process - nf_info->status = NF_STARTING; + nf_init_cfg->status = NF_STARTING; return 0; } - inline static int -onvm_nf_ready(struct onvm_nf_info *info) { +onvm_nf_ready(struct onvm_nf *nf) { // Ensure we've already called nf_start for this NF - if (info->status != NF_STARTING) return -1; + if (nf->status != NF_STARTING) + return -1; - // Register this NF running within its service - info->status = NF_RUNNING; - uint16_t service_count = nf_per_service_count[info->service_id]++; - services[info->service_id][service_count] = info->instance_id; + uint16_t service_count = nf_per_service_count[nf->service_id]++; + services[nf->service_id][service_count] = nf->instance_id; num_nfs++; + // Register this NF running within its service + nf->status = NF_RUNNING; return 0; } - inline static int -onvm_nf_stop(struct onvm_nf_info *nf_info) { +onvm_nf_stop(struct onvm_nf *nf) { uint16_t nf_id; + uint16_t nf_status; uint16_t service_id; + uint16_t nb_pkts, i; int mapIndex; + struct onvm_nf_msg *msg; struct rte_mempool *nf_info_mp; + struct rte_mbuf *pkts[PACKET_READ_SIZE]; + + if (nf == NULL) + return 1; + + nf_id = nf->instance_id; + service_id = nf->service_id; + nf_status = nf->status; + + /* Cleanup the allocated tag */ + if (nf->tag) { + rte_free(nf->tag); + nf->tag = NULL; + } + + /* Cleanup should only happen if NF was starting or running */ + if (nf_status != NF_STARTING && nf_status != NF_RUNNING && nf_status != NF_PAUSED) + return 1; + + nf->status = NF_STOPPED; + nfs[nf->instance_id].status = NF_STOPPED; + + /* Tell parent we stopped running */ + if (nfs[nf_id].thread_info.parent != 0) + rte_atomic16_dec(&nfs[nfs[nf_id].thread_info.parent].thread_info.children_cnt); + + /* Remove the NF from the core it was running on */ + cores[nf->thread_info.core].nf_count--; + cores[nf->thread_info.core].is_dedicated_core = 0; - if(nf_info == NULL || nf_info->status != NF_RUNNING) + /* Clean up possible left over objects in rings */ + while ((nb_pkts = rte_ring_dequeue_burst(nfs[nf_id].rx_q, (void **)pkts, PACKET_READ_SIZE, NULL)) > 0) { + for (i = 0; i < nb_pkts; i++) + rte_pktmbuf_free(pkts[i]); + } + while ((nb_pkts = rte_ring_dequeue_burst(nfs[nf_id].tx_q, (void **)pkts, PACKET_READ_SIZE, NULL)) > 0) { + for (i = 0; i < nb_pkts; i++) + rte_pktmbuf_free(pkts[i]); + } + nf_msg_pool = rte_mempool_lookup(_NF_MSG_POOL_NAME); + while (rte_ring_dequeue(nfs[nf_id].msg_q, (void**)(&msg)) == 0) { + rte_mempool_put(nf_msg_pool, (void*)msg); + } + + /* Free info struct */ + /* Lookup mempool for nf struct */ + nf_info_mp = rte_mempool_lookup(_NF_MEMPOOL_NAME); + if (nf_info_mp == NULL) return 1; - nf_info->status = NF_STOPPED; - nf_id = nf_info->instance_id; - service_id = nf_info->service_id; - cores[nf_info->core].nf_count--; - cores[nf_info->core].is_dedicated_core = 0; + rte_mempool_put(nf_info_mp, (void*)nf); + + /* Further cleanup is only required if NF was succesfully started */ + if (nf_status != NF_RUNNING && nf_status != NF_PAUSED) + return 0; - /* Clean up dangling pointers to info struct */ - nfs[nf_id].info = NULL; + /* Decrease the total number of RUNNING NFs */ + num_nfs--; /* Reset stats */ onvm_stats_clear_nf(nf_id); @@ -289,13 +377,21 @@ onvm_nf_stop(struct onvm_nf_info *nf_info) { } } - /* Free info struct */ - /* Lookup mempool for nf_info struct */ - nf_info_mp = rte_mempool_lookup(_NF_MEMPOOL_NAME); - if (nf_info_mp == NULL) - return 1; + return 0; +} - rte_mempool_put(nf_info_mp, (void*)nf_info); +static void +onvm_nf_init_lpm_region(struct lpm_request *req_lpm) { + struct rte_lpm_config conf; + struct rte_lpm* lpm_region; - return 0; + conf.max_rules = req_lpm->max_num_rules; + conf.number_tbl8s = req_lpm->num_tbl8s; + + lpm_region = rte_lpm_create(req_lpm->name, req_lpm->socket_id, &conf); + if (lpm_region) { + req_lpm->status = 0; + } else { + req_lpm->status = -1; + } } diff --git a/onvm/onvm_mgr/onvm_nf.h b/onvm/onvm_mgr/onvm_nf.h index f8c22d26c..ca809f3af 100644 --- a/onvm/onvm_mgr/onvm_nf.h +++ b/onvm/onvm_mgr/onvm_nf.h @@ -5,9 +5,9 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside - * 2010-2014 Intel Corporation. All rights reserved. + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside + * 2010-2019 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,7 +38,6 @@ * ********************************************************************/ - /****************************************************************************** onvm_nf.h @@ -48,28 +47,22 @@ ******************************************************************************/ - #ifndef _ONVM_NF_H_ #define _ONVM_NF_H_ #include "onvm_threading.h" -extern uint16_t next_instance_id; - - /********************************Interfaces***********************************/ - /* * Interface giving the smallest unsigned integer unused for a NF instance. * - * Output : the unsigned integer + * Output : the unsigned integer * */ uint16_t onvm_nf_next_instance_id(void); - /* * Interface looking through all registered NFs if one needs to start or stop. * @@ -77,7 +70,6 @@ onvm_nf_next_instance_id(void); void onvm_nf_check_status(void); - /* * Interface to send a message to a certain NF. * @@ -90,5 +82,4 @@ onvm_nf_check_status(void); int onvm_nf_send_msg(uint16_t dest, uint8_t msg_type, void *msg_data); - #endif // _ONVM_NF_H_ diff --git a/onvm/onvm_mgr/onvm_pkt.c b/onvm/onvm_mgr/onvm_pkt.c index 75d28b6e5..43a702591 100644 --- a/onvm/onvm_mgr/onvm_pkt.c +++ b/onvm/onvm_mgr/onvm_pkt.c @@ -5,9 +5,9 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside - * 2010-2014 Intel Corporation. All rights reserved. + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside + * 2010-2019 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,7 +38,6 @@ * ********************************************************************/ - /****************************************************************************** onvm_pkt.c @@ -47,14 +46,13 @@ ******************************************************************************/ - #include "onvm_mgr.h" -#include "onvm_pkt.h" + #include "onvm_nf.h" +#include "onvm_pkt.h" /**********************************Interfaces*********************************/ - void onvm_pkt_process_rx_batch(struct queue_mgr *rx_mgr, struct rte_mbuf *pkts[], uint16_t rx_count) { uint16_t i; @@ -67,7 +65,7 @@ onvm_pkt_process_rx_batch(struct queue_mgr *rx_mgr, struct rte_mbuf *pkts[], uin return; for (i = 0; i < rx_count; i++) { - meta = (struct onvm_pkt_meta*) &(((struct rte_mbuf*)pkts[i])->udata64); + meta = (struct onvm_pkt_meta *)&(((struct rte_mbuf *)pkts[i])->udata64); meta->src = 0; meta->chain_index = 0; ret = onvm_flow_dir_get_pkt(pkts[i], &flow_entry); @@ -92,10 +90,9 @@ onvm_pkt_process_rx_batch(struct queue_mgr *rx_mgr, struct rte_mbuf *pkts[], uin onvm_pkt_flush_all_nfs(rx_mgr, NULL); } - void onvm_pkt_flush_all_ports(struct queue_mgr *tx_mgr) { - uint16_t i; + uint16_t i; if (tx_mgr == NULL) return; @@ -104,7 +101,6 @@ onvm_pkt_flush_all_ports(struct queue_mgr *tx_mgr) { onvm_pkt_flush_port_queue(tx_mgr, ports->id[i]); } - void onvm_pkt_drop_batch(struct rte_mbuf **pkts, uint16_t size) { uint16_t i; @@ -115,4 +111,3 @@ onvm_pkt_drop_batch(struct rte_mbuf **pkts, uint16_t size) { for (i = 0; i < size; i++) rte_pktmbuf_free(pkts[i]); } - diff --git a/onvm/onvm_mgr/onvm_pkt.h b/onvm/onvm_mgr/onvm_pkt.h index 1f20cdab2..6c53d90cc 100644 --- a/onvm/onvm_mgr/onvm_pkt.h +++ b/onvm/onvm_mgr/onvm_pkt.h @@ -5,9 +5,9 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside - * 2010-2014 Intel Corporation. All rights reserved. + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside + * 2010-2019 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,7 +38,6 @@ * ********************************************************************/ - /****************************************************************************** onvm_pkt.h @@ -49,14 +48,11 @@ ******************************************************************************/ - #ifndef _ONVM_PKT_H_ #define _ONVM_PKT_H_ - /*********************************Interfaces**********************************/ - /* * Interface to process packets in a given RX queue. * @@ -77,7 +73,6 @@ onvm_pkt_process_rx_batch(struct queue_mgr *rx_mgr, struct rte_mbuf *pkts[], uin void onvm_pkt_flush_all_ports(struct queue_mgr *tx_mgr); - /* * Interface to drop a batch of packets. * @@ -88,5 +83,4 @@ onvm_pkt_flush_all_ports(struct queue_mgr *tx_mgr); void onvm_pkt_drop_batch(struct rte_mbuf **pkts, uint16_t size); - #endif // _ONVM_PKT_H_ diff --git a/onvm/onvm_mgr/onvm_stats.c b/onvm/onvm_mgr/onvm_stats.c index fe20ee8cc..5347545fd 100644 --- a/onvm/onvm_mgr/onvm_stats.c +++ b/onvm/onvm_mgr/onvm_stats.c @@ -5,9 +5,9 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside - * 2010-2014 Intel Corporation. All rights reserved. + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside + * 2010-2019 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,7 +38,6 @@ * ********************************************************************/ - /****************************************************************************** onvm_stats.c @@ -47,16 +46,22 @@ ******************************************************************************/ -#include -#include #include +#include +#include #include "onvm_mgr.h" -#include "onvm_stats.h" #include "onvm_nf.h" +#include "onvm_stats.h" /************************Internal Functions Prototypes************************/ +/* + * Function to send json events to web view + * + */ +static void +onvm_stats_add_event(struct onvm_event *event_info); /* * Function displaying statistics for all ports @@ -67,7 +72,6 @@ static void onvm_stats_display_ports(unsigned difftime, uint8_t verbosity_level); - /* * Function displaying statistics for all NFs * @@ -75,7 +79,6 @@ onvm_stats_display_ports(unsigned difftime, uint8_t verbosity_level); static void onvm_stats_display_nfs(unsigned difftime, uint8_t verbosity_level); - /* * Function clearing the terminal and moving back the cursor to the top left. * @@ -83,7 +86,6 @@ onvm_stats_display_nfs(unsigned difftime, uint8_t verbosity_level); static void onvm_stats_clear_terminal(void); - /* * Function giving the MAC address of a port in string format. * @@ -118,7 +120,6 @@ static FILE *stats_out; static FILE *json_stats_out; static FILE *json_events_out; - /****************************Global variables***************************************/ /* Holds current timestamp, might want to make this not global */ @@ -127,10 +128,10 @@ char buffer[20]; /****************************Interfaces***************************************/ void -onvm_stats_init(uint8_t verbocity_level) { - if (verbocity_level == ONVM_RAW_STATS_DUMP) { - printf("#YYYY-MM-DD HH:MM:SS,nic_rx_pkts,nic_rx_pps,nic_tx_pkts,nic_tx_pps\n"); - printf("#YYYY-MM-DD HH:MM:SS,instance_id,service_id,rx,tx,rx_pps,tx_pps,rx_drop,tx_drop,rx_drop_rate,tx_drop_rate,act_out,act_tonf,act_drop,act_next,act_buffer,act_returned\n"); +onvm_stats_init(uint8_t verbosity_level) { + if (verbosity_level == ONVM_RAW_STATS_DUMP) { + printf("%s", ONVM_STATS_RAW_DUMP_PORT_MSG); + printf("%s", ONVM_STATS_RAW_DUMP_NF_MSG); } } @@ -171,16 +172,19 @@ onvm_stats_cleanup(void) { fclose(stats_out); fclose(json_stats_out); fclose(json_events_out); + /* Delete all JSON objects to free memory */ + cJSON_Delete(onvm_json_root); + cJSON_Delete(onvm_json_events_arr); } } void onvm_stats_display_all(unsigned difftime, uint8_t verbosity_level) { time_t time_raw_format; - struct tm * ptr_time; - time ( &time_raw_format ); - ptr_time = localtime ( &time_raw_format ); - if(strftime(buffer, 20, "%F %T", ptr_time) == 0){ + struct tm *ptr_time; + time(&time_raw_format); + ptr_time = localtime(&time_raw_format); + if (strftime(buffer, 20, "%F %T", ptr_time) == 0) { perror("Couldn't prepare formatted string"); } @@ -203,7 +207,6 @@ onvm_stats_display_all(unsigned difftime, uint8_t verbosity_level) { onvm_stats_flush(); } - void onvm_stats_clear_all_nfs(void) { unsigned i; @@ -223,38 +226,87 @@ onvm_stats_clear_nf(uint16_t id) { } void -onvm_stats_add_event(const char *msg, struct onvm_nf_info *nf_info) { - if (msg == NULL || stats_destination != ONVM_STATS_WEB) { +onvm_stats_gen_event_info(const char *msg, uint8_t type, void *data) { + struct onvm_event *event; + + event = (struct onvm_event *)malloc(sizeof(struct onvm_event)); + if (event == NULL) { + perror("Couldn't allocate event"); + return; + } + + event->type = type; + event->msg = msg; + event->data = data; + + onvm_stats_add_event(event); +} + +void +onvm_stats_gen_event_nf_info(const char *msg, struct onvm_nf *nf) { + struct onvm_event *event; + + event = (struct onvm_event *)malloc(sizeof(struct onvm_event)); + if (event == NULL) { + perror("Couldn't allocate event"); + return; + } + + event->type = ONVM_EVENT_NF_INFO; + event->msg = msg; + event->data = nf; + + onvm_stats_add_event(event); +} + +/****************************Internal functions*******************************/ + +static void +onvm_stats_add_event(struct onvm_event *event_info) { + if (event_info == NULL || stats_destination != ONVM_STATS_WEB) { return; } char event_time_buf[20]; + uint8_t type; + struct tm *ptr_time; + struct onvm_nf *nf; time_t time_raw_format; - struct tm * ptr_time; - time ( &time_raw_format ); - ptr_time = localtime ( &time_raw_format ); + time(&time_raw_format); + type = event_info->type; + + ptr_time = localtime(&time_raw_format); if (strftime(event_time_buf, 20, "%F %T", ptr_time) == 0) { perror("Couldn't prepare formatted string"); } - cJSON* new_event = cJSON_CreateObject(); - cJSON* source = cJSON_CreateObject(); + + cJSON *source = cJSON_CreateObject(); + cJSON *new_event = cJSON_CreateObject(); cJSON_AddStringToObject(new_event, "timestamp", event_time_buf); - cJSON_AddStringToObject(new_event, "message", msg); - if (nf_info == NULL) { + cJSON_AddStringToObject(new_event, "message", event_info->msg); + + if (type == ONVM_EVENT_WITH_CORE) { + cJSON_AddNumberToObject(source, "core", *(int *)(event_info->data)); + } else if (type == ONVM_EVENT_PORT_INFO) { cJSON_AddStringToObject(source, "type", "MGR"); - } else { + } else if (type == ONVM_EVENT_NF_INFO) { + nf = (struct onvm_nf *)event_info->data; + if (nf->tag) + cJSON_AddStringToObject(source, "type", (char *)nf->tag); + else + cJSON_AddStringToObject(source, "type", "NF"); + cJSON_AddNumberToObject(source, "instance_id", (int16_t)nf->instance_id); + cJSON_AddNumberToObject(source, "service_id", (int16_t)nf->service_id); + cJSON_AddNumberToObject(source, "core", (int16_t)nf->thread_info.core); + } else if (type == ONVM_EVENT_NF_STOP) { cJSON_AddStringToObject(source, "type", "NF"); - cJSON_AddNumberToObject(source, "instance_id", (int16_t)nf_info->instance_id); - cJSON_AddNumberToObject(source, "service_id", (int16_t)nf_info->service_id); - } - cJSON_AddItemToObject(new_event, "source", source); + cJSON_AddNumberToObject(source, "instance_id", *(int16_t *)(event_info->data)); + } else + rte_exit(-1, "Invalid stats event type\n"); + cJSON_AddItemToObject(new_event, "source", source); cJSON_AddItemToArray(onvm_json_events_arr, new_event); } - -/****************************Internal functions*******************************/ - - static void onvm_stats_display_ports(unsigned difftime, uint8_t verbosity_level) { unsigned i = 0; @@ -262,7 +314,7 @@ onvm_stats_display_ports(unsigned difftime, uint8_t verbosity_level) { uint64_t nic_tx_pkts = 0; uint64_t nic_rx_pps = 0; uint64_t nic_tx_pps = 0; - char* port_label = NULL; + char *port_label = NULL; /* Arrays to store last TX/RX count to calculate rate */ static uint64_t tx_last[RTE_MAX_ETHPORTS]; static uint64_t rx_last[RTE_MAX_ETHPORTS]; @@ -270,12 +322,12 @@ onvm_stats_display_ports(unsigned difftime, uint8_t verbosity_level) { PORT_MSG[0] = PORT_MSG[1] = "PORTS\n-----\n"; PORT_MSG[2] = ""; - fprintf(stats_out, "%s", PORT_MSG[verbosity_level-1]); + fprintf(stats_out, "%s", PORT_MSG[verbosity_level - 1]); if (verbosity_level != ONVM_RAW_STATS_DUMP) { for (i = 0; i < ports->num_ports; i++) fprintf(stats_out, "Port %u: '%s'\t", (unsigned)ports->id[i], - onvm_stats_print_MAC(ports->id[i])); + onvm_stats_print_MAC(ports->id[i])); fprintf(stats_out, "\n\n"); } for (i = 0; i < ports->num_ports; i++) { @@ -286,31 +338,20 @@ onvm_stats_display_ports(unsigned difftime, uint8_t verbosity_level) { nic_tx_pps = (nic_tx_pkts - tx_last[i]) / difftime; if (verbosity_level == ONVM_RAW_STATS_DUMP) { - fprintf(stats_out, "%s,%u,%"PRIu64",%"PRIu64",%"PRIu64"%"PRIu64"\n", - buffer, - (unsigned)ports->id[i], - nic_rx_pkts, - nic_rx_pps, - nic_tx_pkts, - nic_tx_pps); + fprintf(stats_out, ONVM_STATS_RAW_DUMP_PORTS_CONTENT, buffer, + (unsigned)ports->id[i], nic_rx_pkts, nic_rx_pps, nic_tx_pkts, nic_tx_pps); } else { - fprintf(stats_out, "Port %u - rx: %9"PRIu64" (%9"PRIu64" pps)\t" - "tx: %9"PRIu64" (%9"PRIu64" pps)\n", - (unsigned)ports->id[i], - nic_rx_pkts, - nic_rx_pps, - nic_tx_pkts, - nic_tx_pps); + fprintf(stats_out, ONVM_STATS_REG_PORTS, + (unsigned)ports->id[i], nic_rx_pkts, nic_rx_pps, nic_tx_pkts, nic_tx_pps); } /* Only print this information out if we haven't already printed it to the console above */ if (stats_out != stdout && stats_out != stderr) { ONVM_SNPRINTF(port_label, 8, "Port %d", i); - cJSON_AddItemToObject(onvm_json_port_stats_obj, - port_label, - onvm_json_port_stats[i] = cJSON_CreateObject()); + cJSON_AddItemToObject(onvm_json_port_stats_obj, port_label, + onvm_json_port_stats[i] = cJSON_CreateObject()); cJSON_AddStringToObject(onvm_json_port_stats[i], "Label", port_label); cJSON_AddNumberToObject(onvm_json_port_stats[i], "RX", nic_rx_pps); cJSON_AddNumberToObject(onvm_json_port_stats[i], "TX", nic_tx_pps); @@ -324,10 +365,28 @@ onvm_stats_display_ports(unsigned difftime, uint8_t verbosity_level) { } } +static void +onvm_stats_display_client_wakeup_thread_context(int difftime) { + uint64_t num_wakeups = 0; + uint64_t prev_num_wakeups = 0; + uint64_t wakeup_rate; + unsigned i = 0; + + for (i = 0; i < MAX_NFS; i++) { + if (!onvm_nf_is_valid(&nfs[i])) + continue; + num_wakeups += nf_wakeup_infos[i].num_wakeups; + prev_num_wakeups += nf_wakeup_infos[i].prev_num_wakeups; + nf_wakeup_infos[i].prev_num_wakeups = nf_wakeup_infos[i].num_wakeups; + } + + wakeup_rate = (num_wakeups - prev_num_wakeups) / difftime; + fprintf(stats_out, "Total wakeups = %"PRIu64", Wakeup rate = %"PRIu64"\n", num_wakeups, wakeup_rate); +} static void onvm_stats_display_nfs(unsigned difftime, uint8_t verbosity_level) { - char* nf_label = NULL; + char *nf_label = NULL; unsigned i = 0; /* Arrays to store last TX/RX count for NFs to calculate rate */ static uint64_t nf_tx_last[MAX_NFS]; @@ -337,9 +396,12 @@ onvm_stats_display_nfs(unsigned difftime, uint8_t verbosity_level) { static uint64_t nf_rx_drop_last[MAX_NFS]; static const char *NF_MSG[3]; - NF_MSG[0] = "\nNF IID / SID rx_pps / tx_pps rx_drop / tx_drop out / tonf / drop\n-----------------------------------------------------------------------------------------------\n"; - NF_MSG[1] = "\nNF IID / SID rx_pps / tx_pps rx / tx out / tonf / drop\n" - " drop_pps / drop_pps rx_drop / tx_drop next / buf / ret\n---------------------------------------------------------------------------------------------------\n"; + NF_MSG[0] = ONVM_STATS_MSG; + if (ONVM_NF_SHARE_CORES) { + NF_MSG[1] = ONVM_STATS_SHARED_CORE_MSG; + } else { + NF_MSG[1] = ONVM_STATS_ADV_MSG; + } NF_MSG[2] = ""; /* For same service id TX/RX stats */ @@ -364,7 +426,7 @@ onvm_stats_display_nfs(unsigned difftime, uint8_t verbosity_level) { rx_for_service[i] = 0; tx_for_service[i] = 0; rx_drop_for_service[i] = 0; - tx_drop_for_service[i] = 0;; + tx_drop_for_service[i] = 0; rx_pps_for_service[i] = 0; tx_pps_for_service[i] = 0; rx_drop_rate_for_service[i] = 0; @@ -377,7 +439,7 @@ onvm_stats_display_nfs(unsigned difftime, uint8_t verbosity_level) { act_returned_for_service[i] = 0; } - fprintf(stats_out, "%s", NF_MSG[verbosity_level-1]); + fprintf(stats_out, "%s", NF_MSG[verbosity_level - 1]); for (i = 0; i < MAX_NFS; i++) { if (!onvm_nf_is_valid(&nfs[i])) continue; @@ -391,67 +453,79 @@ onvm_stats_display_nfs(unsigned difftime, uint8_t verbosity_level) { const uint64_t act_next = nfs[i].stats.act_next; const uint64_t act_buffer = nfs[i].stats.tx_buffer; const uint64_t act_returned = nfs[i].stats.tx_returned; - const uint64_t rx_pps = (rx - nf_rx_last[i])/difftime; - const uint64_t tx_pps = (tx - nf_tx_last[i])/difftime; - const uint64_t tx_drop_rate = (tx_drop - nf_tx_drop_last[i])/difftime; - const uint64_t rx_drop_rate = (rx_drop - nf_rx_drop_last[i])/difftime; + const uint64_t rx_pps = (rx - nf_rx_last[i]) / difftime; + const uint64_t tx_pps = (tx - nf_tx_last[i]) / difftime; + const uint64_t tx_drop_rate = (tx_drop - nf_tx_drop_last[i]) / difftime; + const uint64_t rx_drop_rate = (rx_drop - nf_rx_drop_last[i]) / difftime; + const uint64_t num_wakeups = nf_wakeup_infos[i].num_wakeups; + const uint64_t prev_num_wakeups = nf_wakeup_infos[i].prev_num_wakeups; + const uint64_t wakeup_rate = (num_wakeups - prev_num_wakeups) / difftime; + char state; + + uint8_t active = 0; + if (ONVM_NF_SHARE_CORES) + active = rte_atomic16_read(nf_wakeup_infos[i].shm_server); + if (!active) { + state = 'W'; + } else { + state = 'S'; + } /* Save stats for NFs with same service id */ if (print_total_stats) { - rx_for_service[nfs[i].info->service_id] += rx; - tx_for_service[nfs[i].info->service_id] += tx; - rx_drop_for_service[nfs[i].info->service_id] += rx_drop; - tx_drop_for_service[nfs[i].info->service_id] += tx_drop; - rx_pps_for_service[nfs[i].info->service_id] += rx_pps; - tx_pps_for_service[nfs[i].info->service_id] += tx_pps; - rx_drop_rate_for_service[nfs[i].info->service_id] += rx_drop_rate; - tx_drop_rate_for_service[nfs[i].info->service_id] += tx_drop_rate; - act_out_for_service[nfs[i].info->service_id] += act_out; - act_tonf_for_service[nfs[i].info->service_id] += act_tonf; - act_drop_for_service[nfs[i].info->service_id] += act_drop; - act_next_for_service[nfs[i].info->service_id] += act_next; - act_buffer_for_service[nfs[i].info->service_id] += act_buffer; - act_returned_for_service[nfs[i].info->service_id] += act_returned; + rx_for_service[nfs[i].service_id] += rx; + tx_for_service[nfs[i].service_id] += tx; + rx_drop_for_service[nfs[i].service_id] += rx_drop; + tx_drop_for_service[nfs[i].service_id] += tx_drop; + rx_pps_for_service[nfs[i].service_id] += rx_pps; + tx_pps_for_service[nfs[i].service_id] += tx_pps; + rx_drop_rate_for_service[nfs[i].service_id] += rx_drop_rate; + tx_drop_rate_for_service[nfs[i].service_id] += tx_drop_rate; + act_out_for_service[nfs[i].service_id] += act_out; + act_tonf_for_service[nfs[i].service_id] += act_tonf; + act_drop_for_service[nfs[i].service_id] += act_drop; + act_next_for_service[nfs[i].service_id] += act_next; + act_buffer_for_service[nfs[i].service_id] += act_buffer; + act_returned_for_service[nfs[i].service_id] += act_returned; } if (verbosity_level == ONVM_RAW_STATS_DUMP) { - fprintf(stats_out, "%s,%u,%u,%"PRIu64",%"PRIu64",%"PRIu64",%"PRIu64",%"PRIu64",%"PRIu64",%"PRIu64",%"PRIu64",%"PRIu64",%"PRIu64",%"PRIu64",%"PRIu64",%"PRIu64",%"PRIu64"\n", - buffer, nfs[i].info->instance_id, nfs[i].info->service_id, - rx, tx, rx_pps, tx_pps, rx_drop, tx_drop, rx_drop_rate, tx_drop_rate, - act_out, act_tonf, act_drop, act_next, act_buffer, act_returned); + fprintf(stats_out, ONVM_STATS_RAW_DUMP_CONTENT, + buffer, nfs[i].tag, nfs[i].instance_id, nfs[i].service_id, nfs[i].thread_info.core, + nfs[i].thread_info.parent, state, rte_atomic16_read(&nfs[i].thread_info.children_cnt), + rx, tx, rx_pps, tx_pps, rx_drop, tx_drop, rx_drop_rate, tx_drop_rate, + act_out, act_tonf, act_drop, act_next, act_buffer, act_returned, + num_wakeups, wakeup_rate); } else if (verbosity_level == 2) { - fprintf(stats_out, "NF %2u / %-2u - %9"PRIu64" / %-9"PRIu64" %11"PRIu64" / %-11"PRIu64" %11"PRIu64" / %-11"PRIu64" / %-11"PRIu64"\n" - " %9"PRIu64" / %-9"PRIu64" %11"PRIu64" / %-11"PRIu64" %11"PRIu64" / %-11"PRIu64" / %-11"PRIu64"\n", - nfs[i].info->instance_id, nfs[i].info->service_id, - rx_pps, tx_pps, rx, tx, act_out, act_tonf, act_drop, - rx_drop_rate, tx_drop_rate, rx_drop, tx_drop, act_next, act_buffer, act_returned); + fprintf(stats_out, ONVM_STATS_ADV_CONTENT, + nfs[i].tag, nfs[i].instance_id, nfs[i].service_id, nfs[i].thread_info.core, + rx_pps, tx_pps, rx, tx, act_out, act_tonf, act_drop, + nfs[i].thread_info.parent, state, rte_atomic16_read(&nfs[i].thread_info.children_cnt), + rx_drop_rate, tx_drop_rate, rx_drop, tx_drop, act_next, act_buffer, act_returned); + if (ONVM_NF_SHARE_CORES) + fprintf(stats_out, ONVM_STATS_SHARED_CORE_CONTENT, num_wakeups, wakeup_rate); + fprintf(stats_out, "\n"); } else { - fprintf(stats_out, "NF %2u / %-2u - %9"PRIu64" / %-9"PRIu64" %9"PRIu64" / %-9"PRIu64" %11"PRIu64" / %-11"PRIu64" / %-11"PRIu64" \n", - nfs[i].info->instance_id, nfs[i].info->service_id, - rx_pps, tx_pps, rx_drop, tx_drop, act_out, act_tonf, act_drop); + fprintf(stats_out, ONVM_STATS_REG_CONTENT, + nfs[i].tag, nfs[i].instance_id, nfs[i].service_id, nfs[i].thread_info.core, + rx_pps, tx_pps, rx_drop, tx_drop, act_out, act_tonf, act_drop); } /* Only print this information out if we haven't already printed it to the console above */ if (stats_out != stdout && stats_out != stderr) { ONVM_SNPRINTF(nf_label, 6, "NF %d", i); - cJSON_AddItemToObject(onvm_json_nf_stats_obj, - nf_label, - onvm_json_nf_stats[i] = cJSON_CreateObject()); - - cJSON_AddStringToObject(onvm_json_nf_stats[i], - "Label", nf_label); - cJSON_AddNumberToObject(onvm_json_nf_stats[i], - "RX", rx_pps); - cJSON_AddNumberToObject(onvm_json_nf_stats[i], - "TX", tx_pps); - cJSON_AddNumberToObject(onvm_json_nf_stats[i], - "TX_Drop_Rate", tx_drop_rate); - cJSON_AddNumberToObject(onvm_json_nf_stats[i], - "RX_Drop_Rate", rx_drop_rate); - cJSON_AddNumberToObject(onvm_json_nf_stats[i], - "service_id", (int16_t)nfs[i].info->service_id); - cJSON_AddNumberToObject(onvm_json_nf_stats[i], - "instance_id", (int16_t)nfs[i].info->instance_id); + cJSON_AddItemToObject(onvm_json_nf_stats_obj, nf_label, + onvm_json_nf_stats[i] = cJSON_CreateObject()); + + cJSON_AddStringToObject(onvm_json_nf_stats[i], "Label", nf_label); + cJSON_AddNumberToObject(onvm_json_nf_stats[i], "RX", rx_pps); + cJSON_AddNumberToObject(onvm_json_nf_stats[i], "TX", tx_pps); + cJSON_AddNumberToObject(onvm_json_nf_stats[i], "TX_Drop_Rate", tx_drop_rate); + cJSON_AddNumberToObject(onvm_json_nf_stats[i], "RX_Drop_Rate", rx_drop_rate); + cJSON_AddNumberToObject(onvm_json_nf_stats[i], "service_id", (int16_t)nfs[i].service_id); + cJSON_AddNumberToObject(onvm_json_nf_stats[i], "instance_id", + (int16_t)nfs[i].instance_id); + cJSON_AddNumberToObject(onvm_json_nf_stats[i], "core", (int16_t)nfs[i].thread_info.core); free(nf_label); nf_label = NULL; @@ -471,38 +545,43 @@ onvm_stats_display_nfs(unsigned difftime, uint8_t verbosity_level) { fprintf(stats_out, "-----------------\n"); for (i = 0; i < MAX_SERVICES; i++) { uint16_t nfs_for_service = nf_per_service_count[i]; - const char *nf_count = nfs_for_service==1 ? "NF " : "NFs"; + const char *nf_count = nfs_for_service == 1 ? "NF " : "NFs"; if (nfs_for_service == 0) continue; if (verbosity_level == 2) { - fprintf(stats_out, "SID %-2u %2u%s - %9"PRIu64" / %-9"PRIu64" %11"PRIu64" / %-11"PRIu64" %11"PRIu64" / %-11"PRIu64" / %-11"PRIu64"\n" - " %9"PRIu64" / %-9"PRIu64" %11"PRIu64" / %-11"PRIu64" %11"PRIu64" / %-11"PRIu64" / %-11"PRIu64"\n", - i, nfs_for_service, nf_count, - rx_pps_for_service[i], tx_pps_for_service[i], rx_for_service[i], tx_for_service[i], act_out_for_service[i], act_tonf_for_service[i], act_drop_for_service[i], - rx_drop_rate_for_service[i], tx_drop_rate_for_service[i], rx_drop_for_service[i], tx_drop_for_service[i], act_next_for_service[i], act_buffer_for_service[i], act_returned_for_service[i]); + fprintf(stats_out, ONVM_STATS_ADV_TOTALS, + i, nfs_for_service, nf_count, rx_pps_for_service[i], tx_pps_for_service[i], + rx_for_service[i], tx_for_service[i], act_out_for_service[i], + act_tonf_for_service[i], act_drop_for_service[i], rx_drop_rate_for_service[i], + tx_drop_rate_for_service[i], rx_drop_for_service[i], tx_drop_for_service[i], + act_next_for_service[i], act_buffer_for_service[i], act_returned_for_service[i]); } else { - fprintf(stats_out, "SID %-2u %2u%s - %9"PRIu64" / %-9"PRIu64" %9"PRIu64" / %-9"PRIu64" %11"PRIu64" / %-11"PRIu64" / %-11"PRIu64" \n", - i, nfs_for_service, nf_count, - rx_pps_for_service[i], tx_pps_for_service[i], rx_drop_for_service[i], tx_drop_for_service[i], - act_out_for_service[i], act_tonf_for_service[i], act_drop_for_service[i]); + fprintf(stats_out, ONVM_STATS_REG_TOTALS, + i, nfs_for_service, nf_count, rx_pps_for_service[i], tx_pps_for_service[i], + rx_drop_for_service[i], tx_drop_for_service[i], act_out_for_service[i], + act_tonf_for_service[i], act_drop_for_service[i]); } } } -} + if (ONVM_NF_SHARE_CORES) { + fprintf(stats_out, "\n\nShared core stats\n"); + fprintf(stats_out, "-----------------\n"); + onvm_stats_display_client_wakeup_thread_context(difftime); + } -/***************************Helper functions**********************************/ +} +/***************************Helper functions**********************************/ static void onvm_stats_clear_terminal(void) { - const char clr[] = { 27, '[', '2', 'J', '\0' }; - const char topLeft[] = { 27, '[', '1', ';', '1', 'H', '\0' }; + const char clr[] = {27, '[', '2', 'J', '\0'}; + const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0'}; fprintf(stats_out, "%s%s", clr, topLeft); } - static const char * onvm_stats_print_MAC(uint8_t port) { static const char err_address[] = "00:00:00:00:00:00"; @@ -514,17 +593,13 @@ onvm_stats_print_MAC(uint8_t port) { if (unlikely(addresses[port][0] == '\0')) { struct ether_addr mac; rte_eth_macaddr_get(port, &mac); - snprintf(addresses[port], sizeof(addresses[port]), - "%02x:%02x:%02x:%02x:%02x:%02x\n", - mac.addr_bytes[0], mac.addr_bytes[1], - mac.addr_bytes[2], mac.addr_bytes[3], - mac.addr_bytes[4], mac.addr_bytes[5]); + snprintf(addresses[port], sizeof(addresses[port]), "%02x:%02x:%02x:%02x:%02x:%02x\n", mac.addr_bytes[0], + mac.addr_bytes[1], mac.addr_bytes[2], mac.addr_bytes[3], mac.addr_bytes[4], mac.addr_bytes[5]); } return addresses[port]; } - static void onvm_stats_flush(void) { if (stats_out == stdout || stats_out == stderr) { @@ -536,7 +611,6 @@ onvm_stats_flush(void) { fflush(json_events_out); } - static void onvm_stats_truncate(void) { if (stats_out == stdout || stats_out == stderr) { @@ -553,7 +627,6 @@ onvm_stats_truncate(void) { } } - static void onvm_json_reset_objects(void) { time_t current_time; @@ -567,10 +640,8 @@ onvm_json_reset_objects(void) { current_time = time(0); - cJSON_AddStringToObject(onvm_json_root, ONVM_JSON_TIMESTAMP_KEY, - ctime(¤t_time)); + cJSON_AddStringToObject(onvm_json_root, ONVM_JSON_TIMESTAMP_KEY, ctime(¤t_time)); cJSON_AddItemToObject(onvm_json_root, ONVM_JSON_PORT_STATS_KEY, onvm_json_port_stats_obj = cJSON_CreateObject()); - cJSON_AddItemToObject(onvm_json_root, ONVM_JSON_NF_STATS_KEY, - onvm_json_nf_stats_obj = cJSON_CreateObject()); + cJSON_AddItemToObject(onvm_json_root, ONVM_JSON_NF_STATS_KEY, onvm_json_nf_stats_obj = cJSON_CreateObject()); } diff --git a/onvm/onvm_mgr/onvm_stats.h b/onvm/onvm_mgr/onvm_stats.h index aea946951..60d73f74e 100644 --- a/onvm/onvm_mgr/onvm_stats.h +++ b/onvm/onvm_mgr/onvm_stats.h @@ -5,9 +5,9 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside - * 2010-2014 Intel Corporation. All rights reserved. + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside + * 2010-2019 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,7 +38,6 @@ * ********************************************************************/ - /****************************************************************************** onvm_stats.h @@ -47,45 +46,97 @@ ******************************************************************************/ - #ifndef _ONVM_STATS_H_ #define _ONVM_STATS_H_ -#include "cJSON.h" -#include #include +#include +#include "cJSON.h" #define ONVM_STR_STATS_STDOUT "stdout" #define ONVM_STR_STATS_STDERR "stderr" #define ONVM_STR_STATS_WEB "web" +extern const char *NF_MSG[3]; +#define ONVM_STATS_MSG "\n"\ + "NF TAG IID / SID / CORE rx_pps / tx_pps rx_drop / tx_drop out / tonf / drop\n"\ + "----------------------------------------------------------------------------------------------------------------------\n" +#define ONVM_STATS_ADV_MSG "\n"\ + "NF TAG IID / SID / CORE rx_pps / tx_pps rx_drop / tx_drop out / tonf / drop\n"\ + " PNT / S|W / CHLD drop_pps / drop_pps rx_drop / tx_drop next / buf / ret\n"\ + "----------------------------------------------------------------------------------------------------------------------\n" +#define ONVM_STATS_SHARED_CORE_MSG "\n"\ + "NF TAG IID / SID / CORE rx_pps / tx_pps rx_drop / tx_drop out / tonf / drop\n"\ + " PNT / S|W / CHLD drop_pps / drop_pps rx_drop / tx_drop next / buf / ret\n"\ + " wakeups / wakeup_rt\n"\ + "----------------------------------------------------------------------------------------------------------------------\n" +#define ONVM_STATS_RAW_DUMP_PORT_MSG \ + "#YYYY-MM-DD HH:MM:SS,nic_rx_pkts,nic_rx_pps,nic_tx_pkts,nic_tx_pps\n" +#define ONVM_STATS_RAW_DUMP_NF_MSG \ + "#YYYY-MM-DD HH:MM:SS,nf_tag,instance_id,service_id,core,parent,state,children_cnt,"\ + "rx,tx,rx_pps,tx_pps,rx_drop,tx_drop,rx_drop_rate,tx_drop_rate,"\ + "act_out,act_tonf,act_drop,act_next,act_buffer,act_returned,num_wakeups,wakeup_rate\n" +#define ONVM_STATS_REG_CONTENT \ + "%-14s %2u / %-2u / %2u %9" PRIu64 " / %-9" PRIu64 " %11" PRIu64 " / %-11" PRIu64\ + " %11" PRIu64 " / %-11" PRIu64 " / %-11" PRIu64 " \n" +#define ONVM_STATS_REG_TOTALS \ + "SID %-2u %2u%s - %9" PRIu64 " / %-9" PRIu64 " %11" PRIu64\ + " / %-11" PRIu64 " %11" PRIu64 " / %-11" PRIu64 " / %-11" PRIu64 "\n" +#define ONVM_STATS_REG_PORTS \ + "Port %u - rx: %9" PRIu64 " (%9" PRIu64 " pps)\t"\ + "tx: %9" PRIu64 " (%9" PRIu64 " pps)\n" +#define ONVM_STATS_ADV_CONTENT \ + "%-14s %2u / %-2u / %2u %9" PRIu64 " / %-9" PRIu64 " %11" PRIu64 " / %-11" PRIu64\ + " %11" PRIu64 " / %-11" PRIu64 " / %-11" PRIu64\ + "\n %5" PRId16 " / %c / %u %9" PRIu64 " / %-9" PRIu64 " %11" PRIu64 " / %-11" PRIu64\ + " %11" PRIu64 " / %-11" PRIu64 " / %-11" PRIu64 "\n" +#define ONVM_STATS_SHARED_CORE_CONTENT \ + " %11" PRIu64 " / %-11" PRIu64"\n" +#define ONVM_STATS_ADV_TOTALS \ + "SID %-2u %2u%s - %9" PRIu64 " / %-9" PRIu64 " %11" PRIu64\ + " / %-11" PRIu64 " %11" PRIu64 " / %-11" PRIu64 " / %-11" PRIu64\ + "\n %9" PRIu64 " / %-9" PRIu64 " %11" PRIu64\ + " / %-11" PRIu64 " %11" PRIu64 " / %-11" PRIu64 " / %-11" PRIu64 "\n" +#define ONVM_STATS_RAW_DUMP_CONTENT \ + "%s,%s,%u,%u,%u,%u,%c,%u,%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64\ + ",%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64\ + ",%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64 "\n" +#define ONVM_STATS_RAW_DUMP_PORTS_CONTENT \ + "%s,%u,%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64 "\n" + #define ONVM_STATS_FOPEN_ARGS "w+" #define ONVM_STATS_PATH_BASE "../onvm_web/" #define ONVM_JSON_STATS_FILE ONVM_STATS_PATH_BASE "onvm_json_stats.json" #define ONVM_JSON_EVENTS_FILE ONVM_STATS_PATH_BASE "onvm_json_events.json" #define ONVM_STATS_FILE ONVM_STATS_PATH_BASE "onvm_stats.txt" +/* Handle types of web stats events */ +#define ONVM_EVENT_WITH_CORE 0 +#define ONVM_EVENT_PORT_INFO 1 +#define ONVM_EVENT_NF_INFO 2 +#define ONVM_EVENT_NF_STOP 3 + #define ONVM_JSON_PORT_STATS_KEY "onvm_port_stats" #define ONVM_JSON_NF_STATS_KEY "onvm_nf_stats" #define ONVM_JSON_TIMESTAMP_KEY "last_updated" -#define ONVM_SNPRINTF(str_, sz_, fmt_, ...) \ - do { \ - (str_) = (char *)malloc(sizeof(char) * (sz_)); \ - if (!(str_)) \ - rte_exit(-1, "ERROR! [%s,%d]: unable to malloc str.\n", \ - __FUNCTION__, __LINE__); \ - snprintf((str_), (sz_), (fmt_), __VA_ARGS__); \ +#define ONVM_SNPRINTF(str_, sz_, fmt_, ...) \ + do { \ + (str_) = (char*)malloc(sizeof(char) * (sz_)); \ + if (!(str_)) \ + rte_exit(-1, "ERROR! [%s,%d]: unable to malloc str.\n", __FUNCTION__, __LINE__); \ + snprintf((str_), (sz_), (fmt_), __VA_ARGS__); \ } while (0) #define ONVM_RAW_STATS_DUMP 3 -typedef enum { - ONVM_STATS_NONE = 0, - ONVM_STATS_STDOUT, - ONVM_STATS_STDERR, - ONVM_STATS_WEB -} ONVM_STATS_OUTPUT; +typedef enum { ONVM_STATS_NONE = 0, ONVM_STATS_STDOUT, ONVM_STATS_STDERR, ONVM_STATS_WEB } ONVM_STATS_OUTPUT; + +struct onvm_event { + uint8_t type; + const char *msg; + void *data; +}; cJSON* onvm_json_root; cJSON* onvm_json_port_stats_obj; @@ -96,7 +147,6 @@ cJSON* onvm_json_events_arr; /*********************************Interfaces**********************************/ - /* * Function for initializing stats * @@ -106,7 +156,6 @@ cJSON* onvm_json_events_arr; void onvm_stats_init(uint8_t verbosity_level); - /* * Interface called by the manager to tell the stats module where to print * You should only call this once @@ -116,13 +165,15 @@ onvm_stats_init(uint8_t verbosity_level); * browser. If STATS_STDOUT or STATS_STDOUT is specified, then stats will be * output the respective stream. */ -void onvm_stats_set_output(ONVM_STATS_OUTPUT output); +void +onvm_stats_set_output(ONVM_STATS_OUTPUT output); /* * Interface to close out file descriptions and clean up memory * To be called when the stats loop is done */ -void onvm_stats_cleanup(void); +void +onvm_stats_cleanup(void); /* * Interface called by the ONVM Manager to display all statistics @@ -131,8 +182,8 @@ void onvm_stats_cleanup(void); * Input : time passed since last display (to compute packet rate) * */ -void onvm_stats_display_all(unsigned difftime, uint8_t verbosity_level); - +void +onvm_stats_display_all(unsigned difftime, uint8_t verbosity_level); /* * Interface called by the ONVM Manager to clear all NFs statistics @@ -143,8 +194,8 @@ void onvm_stats_display_all(unsigned difftime, uint8_t verbosity_level); * incur a visible slowdown. * */ -void onvm_stats_clear_all_nfs(void); - +void +onvm_stats_clear_all_nfs(void); /* * Interface called by the ONVM Manager to clear one NF's statistics. @@ -152,12 +203,16 @@ void onvm_stats_clear_all_nfs(void); * Input : the NF id * */ -void onvm_stats_clear_nf(uint16_t id); +void +onvm_stats_clear_nf(uint16_t id); /* * Interface called by manager when a new event should be created. */ - void onvm_stats_add_event(const char *msg, struct onvm_nf_info *nf); +void +onvm_stats_gen_event_info(const char *msg, uint8_t type, void *data); +void +onvm_stats_gen_event_nf_info(const char *msg, struct onvm_nf *nf); #endif // _ONVM_STATS_H_ diff --git a/onvm/onvm_nflib/Makefile b/onvm/onvm_nflib/Makefile index 112981f79..69d9f2864 100644 --- a/onvm/onvm_nflib/Makefile +++ b/onvm/onvm_nflib/Makefile @@ -40,6 +40,10 @@ ifeq ($(RTE_SDK),) $(error "Please define RTE_SDK environment variable") endif +ifeq ($(ONVM_HOME),) +$(error "Please define the ONVM_HOME environment variable") +endif + # Default target, can be overriden by command line or environment include $(RTE_SDK)/mk/rte.vars.mk diff --git a/onvm/onvm_nflib/onvm_common.h b/onvm/onvm_nflib/onvm_common.h index eb2ec619e..c2a947def 100644 --- a/onvm/onvm_nflib/onvm_common.h +++ b/onvm/onvm_nflib/onvm_common.h @@ -5,9 +5,9 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside - * 2010-2014 Intel Corporation + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside + * 2010-2019 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -39,57 +39,85 @@ * onvm_common.h - shared data between host and NFs ********************************************************************/ -#ifndef _COMMON_H_ -#define _COMMON_H_ +#ifndef _ONVM_COMMON_H_ +#define _ONVM_COMMON_H_ #include -#include +/* Std C library includes for shared core */ +#include +#include +#include +#include +#include +#include #include +#include -#include "onvm_msg_common.h" #include "onvm_config_common.h" +#include "onvm_msg_common.h" -#define ONVM_MAX_CHAIN_LENGTH 4 // the maximum chain length -#define MAX_NFS 128 // total number of NFs allowed -#define MAX_SERVICES 32 // total number of unique services allowed -#define MAX_NFS_PER_SERVICE 32 // max number of NFs per service. +#define ONVM_MAX_CHAIN_LENGTH 4 // the maximum chain length +#define MAX_NFS 128 // total number of concurrent NFs allowed (-1 because ID 0 is reserved) +#define MAX_SERVICES 32 // total number of unique services allowed +#define MAX_NFS_PER_SERVICE 32 // max number of NFs per service. -#define NUM_MBUFS 32767 // total number of mbufs (2^15 - 1) -#define NF_QUEUE_RINGSIZE 16384 //size of queue for NFs +#define NUM_MBUFS 32767 // total number of mbufs (2^15 - 1) +#define NF_QUEUE_RINGSIZE 16384 // size of queue for NFs #define PACKET_READ_SIZE ((uint16_t)32) -#define ONVM_NF_ACTION_DROP 0 // drop packet -#define ONVM_NF_ACTION_NEXT 1 // to whatever the next action is configured by the SDN controller in the flow table -#define ONVM_NF_ACTION_TONF 2 // send to the NF specified in the argument field (assume it is on the same host) -#define ONVM_NF_ACTION_OUT 3 // send the packet out the NIC port set in the argument field +#define ONVM_NF_SHARE_CORES_DEFAULT 0 // default value for shared core logic, if true NFs sleep while waiting for packets + +#define ONVM_NF_ACTION_DROP 0 // drop packet +#define ONVM_NF_ACTION_NEXT 1 // to whatever the next action is configured by the SDN controller in the flow table +#define ONVM_NF_ACTION_TONF 2 // send to the NF specified in the argument field (assume it is on the same host) +#define ONVM_NF_ACTION_OUT 3 // send the packet out the NIC port set in the argument field + +#define PKT_WAKEUP_THRESHOLD 1 // for shared core mode, how many packets are required to wake up the NF /* Used in setting bit flags for core options */ #define MANUAL_CORE_ASSIGNMENT_BIT 0 #define SHARE_CORE_BIT 1 -//extern uint8_t rss_symmetric_key[40]; +#define ONVM_SIGNAL_TERMINATION -999 -//flag operations that should be used on onvm_pkt_meta +/* Maximum length of NF_TAG including the \0 */ +#define TAG_SIZE 15 + +// flag operations that should be used on onvm_pkt_meta #define ONVM_CHECK_BIT(flags, n) !!((flags) & (1 << (n))) #define ONVM_SET_BIT(flags, n) ((flags) | (1 << (n))) #define ONVM_CLEAR_BIT(flags, n) ((flags) & (0 << (n))) +/* Measured in millions of packets */ +#define PKT_TTL_MULTIPLIER 1000000 +/* Measured in seconds */ +#define TIME_TTL_MULTIPLIER 1 + +/* For NF termination handling */ +#define NF_TERM_WAIT_TIME 1 +#define NF_TERM_INIT_ITER_TIMES 3 +/* If a lot of children spawned this might need to be increased */ +#define NF_TERM_STOP_ITER_TIMES 10 + struct onvm_pkt_meta { - uint8_t action; /* Action to be performed */ + uint8_t action; /* Action to be performed */ uint16_t destination; /* where to go next */ - uint16_t src; /* who processed the packet last */ - uint8_t chain_index; /*index of the current step in the service chain*/ - uint8_t flags; /* bits for custom NF data. Use with caution to prevent collisions from different NFs. */ + uint16_t src; /* who processed the packet last */ + uint8_t chain_index; /*index of the current step in the service chain*/ + uint8_t flags; /* bits for custom NF data. Use with caution to prevent collisions from different NFs. */ }; -static inline struct onvm_pkt_meta* onvm_get_pkt_meta(struct rte_mbuf* pkt) { - return (struct onvm_pkt_meta*)&pkt->udata64; +static inline struct onvm_pkt_meta * +onvm_get_pkt_meta(struct rte_mbuf *pkt) { + return (struct onvm_pkt_meta *)&pkt->udata64; } -static inline uint8_t onvm_get_pkt_chain_index(struct rte_mbuf* pkt) { - return ((struct onvm_pkt_meta*)&pkt->udata64)->chain_index; +static inline uint8_t +onvm_get_pkt_chain_index(struct rte_mbuf *pkt) { + struct onvm_pkt_meta* pkt_meta = (struct onvm_pkt_meta*) &pkt->udata64; + return pkt_meta->chain_index; } /* @@ -105,10 +133,10 @@ static inline uint8_t onvm_get_pkt_chain_index(struct rte_mbuf* pkt) { */ /*******************************Data Structures*******************************/ -/* +/* * Packets may be transported by a tx thread or by an NF. - * This data structure encapsulates data specific to - * tx threads. + * This data structure encapsulates data specific to + * tx threads. */ struct tx_thread_info { unsigned first_nf; @@ -131,7 +159,7 @@ struct packet_buf { * */ struct queue_mgr { unsigned id; - enum {NF, MGR} mgr_type_t; + enum { NF, MGR } mgr_type_t; union { struct tx_thread_info *tx_thread_info; struct packet_buf *to_tx_buf; @@ -139,11 +167,26 @@ struct queue_mgr { struct packet_buf *nf_rx_bufs; }; -struct rx_stats{ +/* NFs wakeup Info: used by manager to update NFs pool and wakeup stats */ +struct wakeup_thread_context { + unsigned first_nf; + unsigned last_nf; +}; + +struct nf_wakeup_info { + const char *sem_name; + sem_t *mutex; + key_t shm_key; + rte_atomic16_t *shm_server; + uint64_t num_wakeups; + uint64_t prev_num_wakeups; +}; + +struct rx_stats { uint64_t rx[RTE_MAX_ETHPORTS]; }; -struct tx_stats{ +struct tx_stats { uint64_t tx[RTE_MAX_ETHPORTS]; uint64_t tx_drop[RTE_MAX_ETHPORTS]; }; @@ -156,59 +199,91 @@ struct port_info { volatile struct tx_stats tx_stats; }; +struct onvm_configuration { + struct { + uint8_t ONVM_NF_SHARE_CORES; + } flags; +}; + struct core_status { uint8_t enabled; uint8_t is_dedicated_core; uint16_t nf_count; }; -struct onvm_nf_info; +struct onvm_nf_local_ctx; +struct onvm_nf; /* Function prototype for NF packet handlers */ -typedef int(*pkt_handler_func)(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, __attribute__ ((unused)) struct onvm_nf_info *nf_info); -/* Function prototype for NF callback handlers */ -typedef int(*callback_handler_func)(__attribute__ ((unused)) struct onvm_nf_info *nf_info); -/* Function prototype for NFs running advanced rings */ -typedef void(*advanced_rings_func)(struct onvm_nf_info *nf_info); +typedef int (*nf_pkt_handler_fn)(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, + __attribute__((unused)) struct onvm_nf_local_ctx *nf_local_ctx); +/* Function prototype for NF the callback */ +typedef int (*nf_user_actions_fn)(__attribute__((unused)) struct onvm_nf_local_ctx *nf_local_ctx); /* Function prototype for NFs that want extra initalization/setup before running */ -typedef void(*setup_func)(struct onvm_nf_info *nf_info); +typedef void (*nf_setup_fn)(struct onvm_nf_local_ctx *nf_local_ctx); +/* Function prototype for NFs to handle custom messages */ +typedef void (*nf_msg_handler_fn)(void *msg_data, struct onvm_nf_local_ctx *nf_local_ctx); +/* Function prototype for NFs to signal handling */ +typedef void (*handle_signal_func)(int); + +/* Contains all functions the NF might use */ +struct onvm_nf_function_table { + nf_setup_fn setup; + nf_msg_handler_fn msg_handler; + nf_user_actions_fn user_actions; + nf_pkt_handler_fn pkt_handler; +}; /* Information needed to initialize a new NF child thread */ struct onvm_nf_scale_info { - struct onvm_nf_info *parent; - uint16_t instance_id; - uint16_t service_id; - uint16_t core; - uint8_t flags; - const char *tag; - void *data; - setup_func setup_func; - pkt_handler_func pkt_func; - callback_handler_func callback_func; - advanced_rings_func adv_rings_func; + struct onvm_nf_init_cfg *nf_init_cfg; + struct onvm_nf *parent; + void * data; + struct onvm_nf_function_table *function_table; +}; + +struct onvm_nf_local_ctx { + struct onvm_nf *nf; + rte_atomic16_t nf_init_finished; + rte_atomic16_t keep_running; + rte_atomic16_t nf_stopped; }; /* - * Define a nf structure with all needed info, including - * stats from the nfs. + * Define a NF structure with all needed info, including: + * thread information, function callbacks, flags, stats and shared core info. + * + * This structure is available in the NF when processing packets or executing the callback. */ struct onvm_nf { struct rte_ring *rx_q; struct rte_ring *tx_q; struct rte_ring *msg_q; - struct onvm_nf_info *info; - uint16_t instance_id; - /* Advanced ring mode or packet handler mode */ - uint8_t nf_mode; - /* Instance ID of parent NF or 0 */ - uint16_t parent; /* Struct for NF to NF communication (NF tx) */ struct queue_mgr *nf_tx_mgr; + uint16_t instance_id; + uint16_t service_id; + uint8_t status; + char *tag; + /* Pointer to NF defined state data */ + void *data; + + struct { + uint16_t core; + /* Instance ID of parent NF or 0 */ + uint16_t parent; + rte_atomic16_t children_cnt; + } thread_info; - /* NF specifc functions */ - pkt_handler_func nf_pkt_function; - callback_handler_func nf_callback_function; - advanced_rings_func nf_advanced_rings_function; - setup_func nf_setup_function; + struct { + uint16_t init_options; + /* If set NF will stop after time reaches time_to_live */ + uint16_t time_to_live; + /* If set NF will stop after pkts TX reach pkt_limit */ + uint16_t pkt_limit; + } flags; + + /* NF specific functions */ + struct onvm_nf_function_table *function_table; /* * Define a structure with stats from the NFs. @@ -232,20 +307,32 @@ struct onvm_nf { volatile uint64_t act_buffer; } stats; + struct { + /* + * Sleep state (shared mem variable) to track state of NF and trigger wakeups + * sleep_state = 1 => NF sleeping (waiting on semaphore) + * sleep_state = 0 => NF running (not waiting on semaphore) + */ + rte_atomic16_t *sleep_state; + /* Mutex for NF sem_wait */ + sem_t *nf_mutex; + } shared_core; }; /* - * Define a structure to describe one NF - * This structure is available in the NF when processing packets or executing the callback. + * The config structure to inialize the NF with onvm_mgr */ -struct onvm_nf_info { +struct onvm_nf_init_cfg { uint16_t instance_id; uint16_t service_id; uint16_t core; - uint8_t flags; + uint16_t init_options; uint8_t status; - const char *tag; - void *data; + char *tag; + /* If set NF will stop after time reaches time_to_live */ + uint16_t time_to_live; + /* If set NF will stop after pkts TX reach pkt_limit */ + uint16_t pkt_limit; }; /* @@ -262,15 +349,25 @@ struct onvm_service_chain { int ref_cnt; }; +struct lpm_request { + char name[64]; + uint32_t max_num_rules; + uint32_t num_tbl8s; + int socket_id; + int status; +}; + /* define common names for structures shared between server and NF */ #define MP_NF_RXQ_NAME "MProc_Client_%u_RX" #define MP_NF_TXQ_NAME "MProc_Client_%u_TX" +#define MP_CLIENT_SEM_NAME "MProc_Client_%u_SEM" #define PKTMBUF_POOL_NAME "MProc_pktmbuf_pool" #define MZ_PORT_INFO "MProc_port_info" #define MZ_CORES_STATUS "MProc_cores_info" -#define MZ_NF_INFO "MProc_nf_info" +#define MZ_NF_INFO "MProc_nf_init_cfg" #define MZ_SERVICES_INFO "MProc_services_info" #define MZ_NF_PER_SERVICE_INFO "MProc_nf_per_service_info" +#define MZ_ONVM_CONFIG "MProc_onvm_config" #define MZ_SCP_INFO "MProc_scp_info" #define MZ_FTP_INFO "MProc_ftp_info" @@ -279,24 +376,28 @@ struct onvm_service_chain { #define _NF_MEMPOOL_NAME "NF_INFO_MEMPOOL" #define _NF_MSG_POOL_NAME "NF_MSG_MEMPOOL" +/* interrupt semaphore specific updates */ +#define SHMSZ 4 // size of shared memory segement (page_size) +#define KEY_PREFIX 123 // prefix len for key + /* common names for NF states */ -#define NF_WAITING_FOR_ID 0 // First step in startup process, doesn't have ID confirmed by manager yet -#define NF_STARTING 1 // When a NF is in the startup process and already has an id -#define NF_RUNNING 2 // Running normally -#define NF_PAUSED 3 // NF is not receiving packets, but may in the future -#define NF_STOPPED 4 // NF has stopped and in the shutdown process -#define NF_ID_CONFLICT 5 // NF is trying to declare an ID already in use -#define NF_NO_IDS 6 // There are no available IDs for this NF -#define NF_SERVICE_MAX 7 // Service ID has exceeded the maximum amount -#define NF_SERVICE_COUNT_MAX 8 // Maximum amount of NF's per service spawned -#define NF_NO_CORES 9 // There are no cores available or specified core can't be used -#define NF_NO_DEDICATED_CORES 10 // There is no space for a dedicated core -#define NF_CORE_OUT_OF_RANGE 11 // The manually selected core is out of range -#define NF_CORE_BUSY 12 // The manually selected core is busy +#define NF_WAITING_FOR_ID 0 // First step in startup process, doesn't have ID confirmed by manager yet +#define NF_STARTING 1 // When a NF is in the startup process and already has an id +#define NF_RUNNING 2 // Running normally +#define NF_PAUSED 3 // NF is not receiving packets, but may in the future +#define NF_STOPPED 4 // NF has stopped and in the shutdown process +#define NF_ID_CONFLICT 5 // NF is trying to declare an ID already in use +#define NF_NO_IDS 6 // There are no available IDs for this NF +#define NF_SERVICE_MAX 7 // Service ID has exceeded the maximum amount +#define NF_SERVICE_COUNT_MAX 8 // Maximum amount of NF's per service spawned +#define NF_NO_CORES 9 // There are no cores available or specified core can't be used +#define NF_NO_DEDICATED_CORES 10 // There is no space for a dedicated core +#define NF_CORE_OUT_OF_RANGE 11 // The manually selected core is out of range +#define NF_CORE_BUSY 12 // The manually selected core is busy +#define NF_WAITING_FOR_LPM 13 // NF is waiting for a LPM request to be fulfilled #define NF_NO_ID -1 -#define ONVM_NF_HANDLE_TX 1 // should be true if NFs primarily pass packets to each other - +#define ONVM_NF_HANDLE_TX 1 // should be true if NFs primarily pass packets to each other /* * Given the rx queue name template above, get the queue name @@ -342,9 +443,42 @@ get_msg_queue_name(unsigned id) { */ static inline int onvm_nf_is_valid(struct onvm_nf *nf) { - return nf && nf->info && nf->info->status == NF_RUNNING; + return nf && nf->status == NF_RUNNING; +} + +/* + * Given the rx queue name template above, get the key of the shared memory + */ +static inline key_t +get_rx_shmkey(unsigned id) { + return KEY_PREFIX * 10 + id; +} + +/* + * Given the sem name template above, get the sem name + */ +static inline const char * +get_sem_name(unsigned id) { + /* buffer for return value. Size calculated by %u being replaced + * by maximum 3 digits (plus an extra byte for safety) */ + static char buffer[sizeof(MP_CLIENT_SEM_NAME) + 2]; + + snprintf(buffer, sizeof(buffer) - 1, MP_CLIENT_SEM_NAME, id); + return buffer; +} + +static inline int +whether_wakeup_client(struct onvm_nf *nf, struct nf_wakeup_info *nf_wakeup_info) { + if (rte_ring_count(nf->rx_q) < PKT_WAKEUP_THRESHOLD) + return 0; + + /* Check if its already woken up */ + if (rte_atomic16_read(nf_wakeup_info->shm_server) == 0) + return 0; + + return 1; } #define RTE_LOGTYPE_APP RTE_LOGTYPE_USER1 -#endif // _COMMON_H_ +#endif // _ONVM_COMMON_H_ diff --git a/onvm/onvm_nflib/onvm_config_common.c b/onvm/onvm_nflib/onvm_config_common.c index 251eaedca..aa8433e8e 100644 --- a/onvm/onvm_nflib/onvm_config_common.c +++ b/onvm/onvm_nflib/onvm_config_common.c @@ -5,8 +5,8 @@ * BSD LICENSE * * Copyright(c) - * 2015-2018 George Washington University - * 2015-2018 University of California Riverside + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -43,13 +43,13 @@ #include #include -#include "onvm_config_common.h" #include "cJSON.h" +#include "onvm_config_common.h" #define IS_NULL_OR_EMPTY_STRING(s) ((s) == NULL || strncmp(s, "", 1) == 0 ? 1 : 0) cJSON* -onvm_config_parse_file(const char* filename){ +onvm_config_parse_file(const char* filename) { if (IS_NULL_OR_EMPTY_STRING(filename)) { return NULL; } @@ -109,7 +109,7 @@ onvm_config_extract_corelist(cJSON* dpdk_config, int* size, int** list) { return -1; } - for(i = 0; i < *size; ++i){ + for (i = 0; i < *size; ++i) { *list[i] = cJSON_GetArrayItem(json_arr, i)->valueint; } @@ -168,12 +168,14 @@ onvm_config_extract_output_location(cJSON* onvm_config, char** output_loc) { return -1; } - local_output_loc = (char *) malloc(sizeof(char) * strlen(cJSON_GetObjectItem(onvm_config, "output")->valuestring)); + local_output_loc = + (char*)malloc(sizeof(char) * strlen(cJSON_GetObjectItem(onvm_config, "output")->valuestring)); if (local_output_loc == NULL) { return -1; } - strncpy(local_output_loc, cJSON_GetObjectItem(onvm_config, "output")->valuestring, strlen(cJSON_GetObjectItem(onvm_config, "output")->valuestring)); + strncpy(local_output_loc, cJSON_GetObjectItem(onvm_config, "output")->valuestring, + strlen(cJSON_GetObjectItem(onvm_config, "output")->valuestring)); *output_loc = local_output_loc; return 0; @@ -277,13 +279,13 @@ onvm_config_create_nf_arg_list(cJSON* config, int* argc, char** argv[]) { /* Add all the argcs toggether, accounting for the program name */ new_argc = 1 + dpdk_argc + onvm_argc; - new_argv = (char **) malloc(sizeof(char*) * new_argc); + new_argv = (char**)malloc(sizeof(char*) * new_argc); if (new_argv == NULL) { printf("Unable to allocate space for new argv\n"); return -1; } - new_argv[0] = (char *) malloc(strlen((*argv)[0])); + new_argv[0] = (char*)malloc(strlen((*argv)[0])); if (new_argv[0] == NULL) { return -1; } @@ -296,7 +298,7 @@ onvm_config_create_nf_arg_list(cJSON* config, int* argc, char** argv[]) { /* Copy the DPDK args to new_argv */ for (i = 0; i < dpdk_argc; ++i) { - new_argv[i+offset] = dpdk_argv[i]; + new_argv[i + offset] = dpdk_argv[i]; } /* DPDK args have been filled in, so offset by that */ @@ -304,27 +306,28 @@ onvm_config_create_nf_arg_list(cJSON* config, int* argc, char** argv[]) { /* Copy ONVM args to new_argv- need to use the offset so that the loop still starts at 0 */ for (i = 0; i < onvm_argc; ++i) { - new_argv[i+offset] = onvm_argv[i]; + new_argv[i + offset] = onvm_argv[i]; } /* Find the dashes in the argv from the original command-line entry */ for (i = 0; i < *argc; ++i) { if (strncmp((*argv)[i], FLAG_DASH, strlen(FLAG_DASH)) == 0) { if (onvm_old_argv_index == 0) { - onvm_old_argv_index = i+1; + onvm_old_argv_index = i + 1; } else { - nf_old_argv_index = i+1; + nf_old_argv_index = i + 1; } } } /* Find the dashes in the newly constructed argv */ for (i = 0; i < new_argc; ++i) { - if (strncmp(new_argv[i], FLAG_DASH, strlen(FLAG_DASH)) == 0 && strlen(new_argv[i]) == strlen(FLAG_DASH)) { + if (strncmp(new_argv[i], FLAG_DASH, strlen(FLAG_DASH)) == 0 && + strlen(new_argv[i]) == strlen(FLAG_DASH)) { if (onvm_new_argv_index == 0) { - onvm_new_argv_index = i+1; + onvm_new_argv_index = i + 1; } else { - nf_new_argv_index = i+1; + nf_new_argv_index = i + 1; } } } @@ -339,23 +342,21 @@ onvm_config_create_nf_arg_list(cJSON* config, int* argc, char** argv[]) { } /* Check the old argv for DPDK values and memcpy the old value to the new_argv array */ - for (i = 1; i < onvm_new_argv_index-1; ++i) { - for (j = 3; j < onvm_old_argv_index-1; ++j) { + for (i = 1; i < onvm_new_argv_index - 1; ++i) { + for (j = 3; j < onvm_old_argv_index - 1; ++j) { if (strncmp((*argv)[j], new_argv[i], strlen(new_argv[i])) == 0 && j % 2 == 1) { - memcpy(new_argv[i+1], (*argv)[j+1], strlen((*argv)[j+1])); + memcpy(new_argv[i + 1], (*argv)[j + 1], strlen((*argv)[j + 1])); } } - } /* Check the old argv for ONVM values and memcpy the old value to the new_argv array */ for (i = onvm_new_argv_index; i < nf_new_argv_index; ++i) { for (j = onvm_old_argv_index; j < nf_old_argv_index; ++j) { if (strncmp((*argv)[j], new_argv[i], strlen(new_argv[i])) == 0 && j % 2 == 0) { - memcpy(new_argv[i+1], (*argv)[j+1], strlen((*argv)[j+1])); + memcpy(new_argv[i + 1], (*argv)[j + 1], strlen((*argv)[j + 1])); } } - } /* Need to clear out the old args so that the NF won't double count them- it should only see NF-specific args */ @@ -371,12 +372,12 @@ onvm_config_create_nf_arg_list(cJSON* config, int* argc, char** argv[]) { int onvm_config_create_onvm_args(cJSON* onvm_config, int* onvm_argc, char** onvm_argv[]) { - char* service_id_string = NULL; - char* instance_id_string = NULL; - int service_id = 0; - int instance_id = 0; + char* service_id_string = NULL; + char* instance_id_string = NULL; + int service_id = 0; + int instance_id = 0; - /* An NF has 2 required ONVM args */ + /* An NF has 2 required ONVM args */ *onvm_argc = 2; if (onvm_config == NULL || onvm_argc == NULL || onvm_argv == NULL) { @@ -393,14 +394,14 @@ onvm_config_create_onvm_args(cJSON* onvm_config, int* onvm_argc, char** onvm_arg *onvm_argc += 2; } - *onvm_argv = (char **) malloc(sizeof(char*) * (*onvm_argc)); + *onvm_argv = (char**)malloc(sizeof(char*) * (*onvm_argc)); if (*onvm_argv == NULL) { printf("Unable to allocate space for onvm_argv\n"); return -1; } /* Allows for MAX_SERVICE_ID_SIZE number of characters in the number */ - service_id_string = (char *) malloc(sizeof(char) * MAX_SERVICE_ID_SIZE); + service_id_string = (char*)malloc(sizeof(char) * MAX_SERVICE_ID_SIZE); if (service_id_string == NULL) { printf("Unable to allocate space for onvm_service_id_string\n"); return -1; @@ -414,11 +415,11 @@ onvm_config_create_onvm_args(cJSON* onvm_config, int* onvm_argc, char** onvm_arg strncpy((*onvm_argv)[0], FLAG_R, strlen(FLAG_R)); - sprintf(service_id_string, "%d", service_id); + snprintf(service_id_string, sizeof(char) * MAX_SERVICE_ID_SIZE, "%d", service_id); (*onvm_argv)[1] = service_id_string; if (*onvm_argc > 2) { - instance_id_string = (char *) malloc(sizeof(char) * MAX_SERVICE_ID_SIZE); + instance_id_string = (char*)malloc(sizeof(char) * MAX_SERVICE_ID_SIZE); if (instance_id_string == NULL) { printf("Unable to allocate space for onvm_instance_id_string\n"); return -1; @@ -430,7 +431,7 @@ onvm_config_create_onvm_args(cJSON* onvm_config, int* onvm_argc, char** onvm_arg return -1; } strncpy((*onvm_argv)[2], FLAG_N, strlen(FLAG_N)); - sprintf(instance_id_string, "%d", instance_id); + snprintf(instance_id_string, sizeof(char) * MAX_SERVICE_ID_SIZE, "%d", instance_id); (*onvm_argv)[3] = instance_id_string; } @@ -443,6 +444,7 @@ onvm_config_create_dpdk_args(cJSON* dpdk_config, int* dpdk_argc, char** dpdk_arg int mem_channels = 0; char* mem_channels_string = NULL; size_t* arg_size = NULL; + size_t mem_channels_string_size; int i = 0; if (dpdk_config == NULL || dpdk_argc == NULL || dpdk_argv == NULL) { @@ -454,37 +456,39 @@ onvm_config_create_dpdk_args(cJSON* dpdk_config, int* dpdk_argc, char** dpdk_arg /* DPKD requires 6 args */ *dpdk_argc = 6; - *dpdk_argv = (char **) malloc(sizeof(char*) * (*dpdk_argc)); + *dpdk_argv = (char**)malloc(sizeof(char*) * (*dpdk_argc)); if (*dpdk_argv == NULL) { printf("Unable to allocate space for dpdk_argv\n"); return -1; } - arg_size = (size_t *) malloc(sizeof(size_t) * (*dpdk_argc)); + arg_size = (size_t*)malloc(sizeof(size_t) * (*dpdk_argc)); if (arg_size == NULL) { return -1; } - core_string = (char *) malloc(sizeof(char) * strlen(cJSON_GetObjectItem(dpdk_config, "corelist")->valuestring)); + core_string = (char*)malloc(sizeof(char) * strlen(cJSON_GetObjectItem(dpdk_config, "corelist")->valuestring)); if (core_string == NULL) { printf("Unable to allocate space for core string\n"); return -1; } - core_string = strncpy(core_string, cJSON_GetObjectItem(dpdk_config, "corelist")->valuestring, strlen(cJSON_GetObjectItem(dpdk_config, "corelist")->valuestring)); + core_string = strncpy(core_string, cJSON_GetObjectItem(dpdk_config, "corelist")->valuestring, + strlen(cJSON_GetObjectItem(dpdk_config, "corelist")->valuestring)); if (onvm_config_extract_memory_channels(dpdk_config, &mem_channels) < 0) { printf("Unable to extract memory channels\n"); return -1; } - mem_channels_string = (char *) malloc(sizeof(char) * 3); + mem_channels_string_size = sizeof(char) * 3; + mem_channels_string = (char*)malloc(mem_channels_string_size); if (mem_channels_string == NULL) { printf("Unable to allocate space for memory channels string\n"); return -1; } - sprintf(mem_channels_string, "%d", mem_channels); + snprintf(mem_channels_string, mem_channels_string_size, "%d", mem_channels); arg_size[0] = strlen(FLAG_L); arg_size[1] = strlen(core_string); @@ -494,7 +498,7 @@ onvm_config_create_dpdk_args(cJSON* dpdk_config, int* dpdk_argc, char** dpdk_arg arg_size[5] = strlen(FLAG_DASH); for (i = 0; i < *dpdk_argc; ++i) { - (*dpdk_argv)[i] = (char *) malloc(arg_size[i]); + (*dpdk_argv)[i] = (char*)malloc(arg_size[i]); if ((*dpdk_argv)[i] == NULL) { return -1; diff --git a/onvm/onvm_nflib/onvm_config_common.h b/onvm/onvm_nflib/onvm_config_common.h index 48bf922ea..f4de86aec 100644 --- a/onvm/onvm_nflib/onvm_config_common.h +++ b/onvm/onvm_nflib/onvm_config_common.h @@ -5,9 +5,9 @@ * BSD LICENSE * * Copyright(c) - * 2015-2016 George Washington University - * 2015-2016 University of California Riverside - * 2016 Hewlett Packard Enterprise Development LP + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside + * 2016-2019 Hewlett Packard Enterprise Development LP * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -55,7 +55,6 @@ /*****************************API************************************/ - /** * Parses a config file into a cJSON struct. * This struct contains all information stored within the config file @@ -206,4 +205,4 @@ onvm_config_create_onvm_args(cJSON* onvm_config, int* onvm_argc, char** onvm_arg int onvm_config_create_dpdk_args(cJSON* dpdk_config, int* dpdk_argc, char** dpdk_argv[]); -#endif +#endif // _ONVM_CONFIG_COMMON_H_ diff --git a/onvm/onvm_nflib/onvm_flow_dir.c b/onvm/onvm_nflib/onvm_flow_dir.c index f5eba6daf..41f13f9dc 100644 --- a/onvm/onvm_nflib/onvm_flow_dir.c +++ b/onvm/onvm_nflib/onvm_flow_dir.c @@ -5,8 +5,8 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,16 +38,16 @@ * onvm_flow_dir.c - flow director APIs ********************************************************************/ -#include -#include -#include +#include "onvm_flow_dir.h" +#include +#include #include #include -#include -#include +#include +#include +#include #include "onvm_common.h" #include "onvm_flow_table.h" -#include "onvm_flow_dir.h" #define NO_FLAGS 0 #define SDN_FT_ENTRIES 1024 @@ -56,16 +56,14 @@ struct onvm_ft *sdn_ft; struct onvm_ft **sdn_ft_p; int -onvm_flow_dir_init(void) -{ - const struct rte_memzone *mz_ftp; +onvm_flow_dir_init(void) { + const struct rte_memzone *mz_ftp; - sdn_ft = onvm_ft_create(SDN_FT_ENTRIES, sizeof(struct onvm_flow_entry)); - if(sdn_ft == NULL) { + sdn_ft = onvm_ft_create(SDN_FT_ENTRIES, sizeof(struct onvm_flow_entry)); + if (sdn_ft == NULL) { rte_exit(EXIT_FAILURE, "Unable to create flow table\n"); } - mz_ftp = rte_memzone_reserve(MZ_FTP_INFO, sizeof(struct onvm_ft *), - rte_socket_id(), NO_FLAGS); + mz_ftp = rte_memzone_reserve(MZ_FTP_INFO, sizeof(struct onvm_ft *), rte_socket_id(), NO_FLAGS); if (mz_ftp == NULL) { rte_exit(EXIT_FAILURE, "Canot reserve memory zone for flow table pointer\n"); } @@ -73,13 +71,12 @@ onvm_flow_dir_init(void) sdn_ft_p = mz_ftp->addr; *sdn_ft_p = sdn_ft; - return 0; + return 0; } int -onvm_flow_dir_nf_init(void) -{ - const struct rte_memzone *mz_ftp; +onvm_flow_dir_nf_init(void) { + const struct rte_memzone *mz_ftp; struct onvm_ft **ftp; mz_ftp = rte_memzone_lookup(MZ_FTP_INFO); @@ -88,75 +85,75 @@ onvm_flow_dir_nf_init(void) ftp = mz_ftp->addr; sdn_ft = *ftp; - return 0; + return 0; } int -onvm_flow_dir_get_pkt( struct rte_mbuf *pkt, struct onvm_flow_entry **flow_entry){ - int ret; - ret = onvm_ft_lookup_pkt(sdn_ft, pkt, (char **)flow_entry); +onvm_flow_dir_get_pkt(struct rte_mbuf *pkt, struct onvm_flow_entry **flow_entry) { + int ret; + ret = onvm_ft_lookup_pkt(sdn_ft, pkt, (char **)flow_entry); - return ret; + return ret; } int -onvm_flow_dir_add_pkt(struct rte_mbuf *pkt, struct onvm_flow_entry **flow_entry){ - int ret; - ret = onvm_ft_add_pkt(sdn_ft, pkt, (char**)flow_entry); +onvm_flow_dir_add_pkt(struct rte_mbuf *pkt, struct onvm_flow_entry **flow_entry) { + int ret; + ret = onvm_ft_add_pkt(sdn_ft, pkt, (char **)flow_entry); - return ret; + return ret; } int -onvm_flow_dir_del_pkt(struct rte_mbuf* pkt){ - int ret; - struct onvm_flow_entry *flow_entry; - int ref_cnt; +onvm_flow_dir_del_pkt(struct rte_mbuf *pkt) { + int ret; + struct onvm_flow_entry *flow_entry; + int ref_cnt; ret = onvm_flow_dir_get_pkt(pkt, &flow_entry); - if (ret >= 0) { - ref_cnt = flow_entry->sc->ref_cnt--; - if (ref_cnt <= 0) { - ret = onvm_flow_dir_del_and_free_pkt(pkt); - } - } - - return ret; + if (ret >= 0) { + ref_cnt = flow_entry->sc->ref_cnt--; + if (ref_cnt <= 0) { + ret = onvm_flow_dir_del_and_free_pkt(pkt); + } + } + + return ret; } int -onvm_flow_dir_del_and_free_pkt(struct rte_mbuf *pkt){ - int ret; - struct onvm_flow_entry *flow_entry; - - ret = onvm_flow_dir_get_pkt(pkt, &flow_entry); - if (ret >= 0) { - rte_free(flow_entry->sc); - rte_free(flow_entry->key); - ret = onvm_ft_remove_pkt(sdn_ft, pkt); - } - - return ret; +onvm_flow_dir_del_and_free_pkt(struct rte_mbuf *pkt) { + int ret; + struct onvm_flow_entry *flow_entry; + + ret = onvm_flow_dir_get_pkt(pkt, &flow_entry); + if (ret >= 0) { + rte_free(flow_entry->sc); + rte_free(flow_entry->key); + ret = onvm_ft_remove_pkt(sdn_ft, pkt); + } + + return ret; } int -onvm_flow_dir_get_key(struct onvm_ft_ipv4_5tuple *key, struct onvm_flow_entry **flow_entry){ - int ret; +onvm_flow_dir_get_key(struct onvm_ft_ipv4_5tuple *key, struct onvm_flow_entry **flow_entry) { + int ret; ret = onvm_ft_lookup_key(sdn_ft, key, (char **)flow_entry); return ret; } int -onvm_flow_dir_add_key(struct onvm_ft_ipv4_5tuple *key, struct onvm_flow_entry **flow_entry){ +onvm_flow_dir_add_key(struct onvm_ft_ipv4_5tuple *key, struct onvm_flow_entry **flow_entry) { int ret; - ret = onvm_ft_add_key(sdn_ft, key, (char**)flow_entry); + ret = onvm_ft_add_key(sdn_ft, key, (char **)flow_entry); return ret; } int -onvm_flow_dir_del_key(struct onvm_ft_ipv4_5tuple *key){ +onvm_flow_dir_del_key(struct onvm_ft_ipv4_5tuple *key) { int ret; struct onvm_flow_entry *flow_entry; int ref_cnt; @@ -173,7 +170,7 @@ onvm_flow_dir_del_key(struct onvm_ft_ipv4_5tuple *key){ } int -onvm_flow_dir_del_and_free_key(struct onvm_ft_ipv4_5tuple *key){ +onvm_flow_dir_del_and_free_key(struct onvm_ft_ipv4_5tuple *key) { int ret; struct onvm_flow_entry *flow_entry; diff --git a/onvm/onvm_nflib/onvm_flow_dir.h b/onvm/onvm_nflib/onvm_flow_dir.h index 3e7deada9..0f75f6dc1 100644 --- a/onvm/onvm_nflib/onvm_flow_dir.h +++ b/onvm/onvm_nflib/onvm_flow_dir.h @@ -5,8 +5,8 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -44,12 +44,12 @@ #include "onvm_common.h" #include "onvm_flow_table.h" -extern struct onvm_ft *sdn_ft; -extern struct onvm_ft **sdn_ft_p; +extern struct onvm_ft* sdn_ft; +extern struct onvm_ft** sdn_ft_p; struct onvm_flow_entry { - struct onvm_ft_ipv4_5tuple *key; - struct onvm_service_chain *sc; + struct onvm_ft_ipv4_5tuple* key; + struct onvm_service_chain* sc; uint64_t ref_cnt; uint16_t idle_timeout; uint16_t hard_timeout; @@ -62,16 +62,27 @@ struct onvm_flow_entry { * 0 on success. *flow_entry points to this packet flow's flow entry * -ENOENT if flow has not been added to table. *flow_entry points to flow entry */ -int onvm_flow_dir_init(void); -int onvm_flow_dir_nf_init(void); -int onvm_flow_dir_get_pkt(struct rte_mbuf* pkt, struct onvm_flow_entry **flow_entry); -int onvm_flow_dir_add_pkt(struct rte_mbuf* pkt, struct onvm_flow_entry **flow_entry); -/* delete the flow dir entry, but do not free the service chain (useful if a service chain is pointed to by several different flows */ -int onvm_flow_dir_del_pkt(struct rte_mbuf* pkt); +int +onvm_flow_dir_init(void); +int +onvm_flow_dir_nf_init(void); +int +onvm_flow_dir_get_pkt(struct rte_mbuf* pkt, struct onvm_flow_entry** flow_entry); +int +onvm_flow_dir_add_pkt(struct rte_mbuf* pkt, struct onvm_flow_entry** flow_entry); +/* delete the flow dir entry, but do not free the service chain (useful if a service chain is pointed to by several + * different flows */ +int +onvm_flow_dir_del_pkt(struct rte_mbuf* pkt); /* Delete the flow dir entry and free the service chain */ -int onvm_flow_dir_del_and_free_pkt(struct rte_mbuf* pkt); -int onvm_flow_dir_get_key(struct onvm_ft_ipv4_5tuple* key, struct onvm_flow_entry **flow_entry); -int onvm_flow_dir_add_key(struct onvm_ft_ipv4_5tuple* key, struct onvm_flow_entry **flow_entry); -int onvm_flow_dir_del_key(struct onvm_ft_ipv4_5tuple* key); -int onvm_flow_dir_del_and_free_key(struct onvm_ft_ipv4_5tuple* key); -#endif // _ONVM_FLOW_DIR_H_ +int +onvm_flow_dir_del_and_free_pkt(struct rte_mbuf* pkt); +int +onvm_flow_dir_get_key(struct onvm_ft_ipv4_5tuple* key, struct onvm_flow_entry** flow_entry); +int +onvm_flow_dir_add_key(struct onvm_ft_ipv4_5tuple* key, struct onvm_flow_entry** flow_entry); +int +onvm_flow_dir_del_key(struct onvm_ft_ipv4_5tuple* key); +int +onvm_flow_dir_del_and_free_key(struct onvm_ft_ipv4_5tuple* key); +#endif // _ONVM_FLOW_DIR_H_ diff --git a/onvm/onvm_nflib/onvm_flow_table.c b/onvm/onvm_nflib/onvm_flow_table.c index a95b0e01b..c993cb86f 100644 --- a/onvm/onvm_nflib/onvm_flow_table.c +++ b/onvm/onvm_nflib/onvm_flow_table.c @@ -5,8 +5,8 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,31 +38,26 @@ * onvm_flow_table.c - a generic flow table ********************************************************************/ +#include #include #include #include -#include #include #include "onvm_flow_table.h" -uint8_t rss_symmetric_key[40] = { 0x6d, 0x5a, 0x6d, 0x5a, - 0x6d, 0x5a, 0x6d, 0x5a, - 0x6d, 0x5a, 0x6d, 0x5a, - 0x6d, 0x5a, 0x6d, 0x5a, - 0x6d, 0x5a, 0x6d, 0x5a, - 0x6d, 0x5a, 0x6d, 0x5a, - 0x6d, 0x5a, 0x6d, 0x5a, - 0x6d, 0x5a, 0x6d, 0x5a, - 0x6d, 0x5a, 0x6d, 0x5a, - 0x6d, 0x5a, 0x6d, 0x5a,}; +uint8_t rss_symmetric_key[40] = { + 0x6d, 0x5a, 0x6d, 0x5a, 0x6d, 0x5a, 0x6d, 0x5a, 0x6d, 0x5a, 0x6d, 0x5a, 0x6d, 0x5a, + 0x6d, 0x5a, 0x6d, 0x5a, 0x6d, 0x5a, 0x6d, 0x5a, 0x6d, 0x5a, 0x6d, 0x5a, 0x6d, 0x5a, + 0x6d, 0x5a, 0x6d, 0x5a, 0x6d, 0x5a, 0x6d, 0x5a, 0x6d, 0x5a, 0x6d, 0x5a, +}; /* Create a new flow table made of an rte_hash table and a fixed size * data array for storing values. Only supports IPv4 5-tuple lookups. */ -struct onvm_ft* +struct onvm_ft * onvm_ft_create(int cnt, int entry_size) { - struct rte_hash* hash; - struct onvm_ft* ft; + struct rte_hash *hash; + struct onvm_ft *ft; struct rte_hash_parameters ipv4_hash_params = { .name = NULL, .entries = cnt, @@ -75,12 +70,12 @@ onvm_ft_create(int cnt, int entry_size) { /* create ipv4 hash table. use core number and cycle counter to get a unique name. */ ipv4_hash_params.name = s; ipv4_hash_params.socket_id = rte_socket_id(); - snprintf(s, sizeof(s), "onvm_ft_%d-%"PRIu64, rte_lcore_id(), rte_get_tsc_cycles()); + snprintf(s, sizeof(s), "onvm_ft_%d-%" PRIu64, rte_lcore_id(), rte_get_tsc_cycles()); hash = rte_hash_create(&ipv4_hash_params); if (hash == NULL) { return NULL; } - ft = (struct onvm_ft*)rte_calloc("table", 1, sizeof(struct onvm_ft), 0); + ft = (struct onvm_ft *)rte_calloc("table", 1, sizeof(struct onvm_ft), 0); if (ft == NULL) { rte_hash_free(hash); return NULL; @@ -106,7 +101,7 @@ onvm_ft_create(int cnt, int entry_size) { -ENOSPC if there is no space in the hash for this key. */ int -onvm_ft_add_pkt(struct onvm_ft* table, struct rte_mbuf *pkt, char** data) { +onvm_ft_add_pkt(struct onvm_ft *table, struct rte_mbuf *pkt, char **data) { int32_t tbl_index; struct onvm_ft_ipv4_5tuple key; int err; @@ -117,7 +112,7 @@ onvm_ft_add_pkt(struct onvm_ft* table, struct rte_mbuf *pkt, char** data) { } tbl_index = rte_hash_add_key_with_hash(table->hash, (const void *)&key, pkt->hash.rss); if (tbl_index >= 0) { - *data = &table->data[tbl_index*table->entry_size]; + *data = &table->data[tbl_index * table->entry_size]; } return tbl_index; } @@ -129,7 +124,7 @@ onvm_ft_add_pkt(struct onvm_ft* table, struct rte_mbuf *pkt, char** data) { -EINVAL if the parameters are invalid. */ int -onvm_ft_lookup_pkt(struct onvm_ft* table, struct rte_mbuf *pkt, char** data) { +onvm_ft_lookup_pkt(struct onvm_ft *table, struct rte_mbuf *pkt, char **data) { int32_t tbl_index; struct onvm_ft_ipv4_5tuple key; int ret; @@ -147,7 +142,8 @@ onvm_ft_lookup_pkt(struct onvm_ft* table, struct rte_mbuf *pkt, char** data) { /* Removes an entry from the flow table Returns: - A positive value that can be used by the caller as an offset into an array of user data. This value is unique for this key, and is the same value that was returned when the key was added. + A positive value that can be used by the caller as an offset into an array of user data. This value is unique for + this key, and is the same value that was returned when the key was added. -ENOENT if the key is not found. -EINVAL if the parameters are invalid. */ @@ -164,33 +160,33 @@ onvm_ft_remove_pkt(struct onvm_ft *table, struct rte_mbuf *pkt) { } int -onvm_ft_add_key(struct onvm_ft* table, struct onvm_ft_ipv4_5tuple *key, char** data) { +onvm_ft_add_key(struct onvm_ft *table, struct onvm_ft_ipv4_5tuple *key, char **data) { int32_t tbl_index; - uint32_t softrss; + uint32_t softrss; - softrss = onvm_softrss(key); + softrss = onvm_softrss(key); tbl_index = rte_hash_add_key_with_hash(table->hash, (const void *)key, softrss); if (tbl_index >= 0) { - *data = onvm_ft_get_data(table, tbl_index); + *data = onvm_ft_get_data(table, tbl_index); } return tbl_index; } int -onvm_ft_lookup_key(struct onvm_ft* table, struct onvm_ft_ipv4_5tuple *key, char** data) { +onvm_ft_lookup_key(struct onvm_ft *table, struct onvm_ft_ipv4_5tuple *key, char **data) { int32_t tbl_index; - uint32_t softrss; + uint32_t softrss; - softrss = onvm_softrss(key); + softrss = onvm_softrss(key); tbl_index = rte_hash_lookup_with_hash(table->hash, (const void *)key, softrss); - if (tbl_index >= 0) { + if (tbl_index >= 0) { *data = onvm_ft_get_data(table, tbl_index); } - return tbl_index; + return tbl_index; } int32_t @@ -205,7 +201,8 @@ onvm_ft_remove_key(struct onvm_ft *table, struct onvm_ft_ipv4_5tuple *key) { Parameters: key: Output containing the key where current iterator was pointing at data: Output containing the data associated with key. Returns NULL if data was not stored. - next: Pointer to iterator. Should be 0 to start iterating the hash table. Iterator is incremented after each call of this function. + next: Pointer to iterator. Should be 0 to start iterating the hash table. Iterator is incremented after each call + of this function. Returns: Position where key was stored, if successful. -EINVAL if the parameters are invalid. diff --git a/onvm/onvm_nflib/onvm_flow_table.h b/onvm/onvm_nflib/onvm_flow_table.h index 761c9f740..b6d5a25ca 100644 --- a/onvm/onvm_nflib/onvm_flow_table.h +++ b/onvm/onvm_nflib/onvm_flow_table.h @@ -5,8 +5,8 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -41,29 +41,29 @@ #ifndef _ONVM_FLOW_TABLE_H_ #define _ONVM_FLOW_TABLE_H_ -#include -#include #include #include +#include #include -#include #include -#include "onvm_pkt_helper.h" +#include +#include #include "onvm_common.h" +#include "onvm_pkt_helper.h" extern uint8_t rss_symmetric_key[40]; #ifdef RTE_MACHINE_CPUFLAG_SSE4_2 #include -#define DEFAULT_HASH_FUNC rte_hash_crc +#define DEFAULT_HASH_FUNC rte_hash_crc #else #include -#define DEFAULT_HASH_FUNC rte_jhash +#define DEFAULT_HASH_FUNC rte_jhash #endif struct onvm_ft { - struct rte_hash* hash; - char* data; + struct rte_hash *hash; + char *data; int cnt; int entry_size; }; @@ -73,7 +73,7 @@ struct onvm_ft_ipv4_5tuple { uint32_t dst_addr; uint16_t src_port; uint16_t dst_port; - uint8_t proto; + uint8_t proto; }; /* from l2_forward example, but modified to include port. This should @@ -81,8 +81,8 @@ struct onvm_ft_ipv4_5tuple { * the struct in 4byte chunks. */ union ipv4_5tuple_host { struct { - uint8_t pad0; - uint8_t proto; + uint8_t pad0; + uint8_t proto; uint16_t virt_port; uint32_t ip_src; uint32_t ip_dst; @@ -92,7 +92,7 @@ union ipv4_5tuple_host { __m128i xmm; }; -struct onvm_ft* +struct onvm_ft * onvm_ft_create(int cnt, int entry_size); int @@ -105,10 +105,10 @@ int32_t onvm_ft_remove_pkt(struct onvm_ft *table, struct rte_mbuf *pkt); int -onvm_ft_add_key(struct onvm_ft* table, struct onvm_ft_ipv4_5tuple *key, char** data); +onvm_ft_add_key(struct onvm_ft *table, struct onvm_ft_ipv4_5tuple *key, char **data); int -onvm_ft_lookup_key(struct onvm_ft* table, struct onvm_ft_ipv4_5tuple *key, char** data); +onvm_ft_lookup_key(struct onvm_ft *table, struct onvm_ft_ipv4_5tuple *key, char **data); int32_t onvm_ft_remove_key(struct onvm_ft *table, struct onvm_ft_ipv4_5tuple *key); @@ -126,18 +126,16 @@ onvm_ft_free(struct onvm_ft *table); static inline void _onvm_ft_print_key(struct onvm_ft_ipv4_5tuple *key) { - printf("IP: %" PRIu8 ".%" PRIu8 ".%" PRIu8 ".%" PRIu8, - key->src_addr & 0xFF, (key->src_addr >> 8) & 0xFF, - (key->src_addr >> 16) & 0xFF, (key->src_addr >> 24) & 0xFF); - printf("-%" PRIu8 ".%" PRIu8 ".%" PRIu8 ".%" PRIu8 " ", - key->dst_addr & 0xFF, (key->dst_addr >> 8) & 0xFF, - (key->dst_addr >> 16) & 0xFF, (key->dst_addr >> 24) & 0xFF); + printf("IP: %" PRIu8 ".%" PRIu8 ".%" PRIu8 ".%" PRIu8, key->src_addr & 0xFF, (key->src_addr >> 8) & 0xFF, + (key->src_addr >> 16) & 0xFF, (key->src_addr >> 24) & 0xFF); + printf("-%" PRIu8 ".%" PRIu8 ".%" PRIu8 ".%" PRIu8 " ", key->dst_addr & 0xFF, (key->dst_addr >> 8) & 0xFF, + (key->dst_addr >> 16) & 0xFF, (key->dst_addr >> 24) & 0xFF); printf("Port: %d %d Proto: %d\n", key->src_port, key->dst_port, key->proto); } -static inline char* -onvm_ft_get_data(struct onvm_ft* table, int32_t index) { - return &table->data[index*table->entry_size]; +static inline char * +onvm_ft_get_data(struct onvm_ft *table, int32_t index) { + return &table->data[index * table->entry_size]; } static inline int @@ -151,7 +149,7 @@ onvm_ft_fill_key(struct onvm_ft_ipv4_5tuple *key, struct rte_mbuf *pkt) { } ipv4_hdr = onvm_pkt_ipv4_hdr(pkt); memset(key, 0, sizeof(struct onvm_ft_ipv4_5tuple)); - key->proto = ipv4_hdr->next_proto_id; + key->proto = ipv4_hdr->next_proto_id; key->src_addr = ipv4_hdr->src_addr; key->dst_addr = ipv4_hdr->dst_addr; if (key->proto == IP_PROTOCOL_TCP) { @@ -162,7 +160,7 @@ onvm_ft_fill_key(struct onvm_ft_ipv4_5tuple *key, struct rte_mbuf *pkt) { udp_hdr = onvm_pkt_udp_hdr(pkt); key->src_port = udp_hdr->src_port; key->dst_port = udp_hdr->dst_port; - } else { + } else { key->src_port = 0; key->dst_port = 0; } @@ -175,26 +173,24 @@ onvm_ft_fill_key_symmetric(struct onvm_ft_ipv4_5tuple *key, struct rte_mbuf *pkt return -EPROTONOSUPPORT; } - if (key->dst_addr > key->src_addr) { + if (key->dst_addr > key->src_addr) { uint32_t temp = key->dst_addr; key->dst_addr = key->src_addr; key->src_addr = temp; - } + } - if (key->dst_port > key->src_port) { + if (key->dst_port > key->src_port) { uint16_t temp = key->dst_port; key->dst_port = key->src_port; key->src_port = temp; - } + } - return 0; + return 0; } /* Hash a flow key to get an int. From L3 fwd example */ static inline uint32_t -onvm_ft_ipv4_hash_crc(const void *data, __rte_unused uint32_t data_len, - uint32_t init_val) -{ +onvm_ft_ipv4_hash_crc(const void *data, __rte_unused uint32_t data_len, uint32_t init_val) { const union ipv4_5tuple_host *k; uint32_t t; const uint32_t *p; @@ -208,7 +204,7 @@ onvm_ft_ipv4_hash_crc(const void *data, __rte_unused uint32_t data_len, init_val = rte_hash_crc_4byte(k->ip_src, init_val); init_val = rte_hash_crc_4byte(k->ip_dst, init_val); init_val = rte_hash_crc_4byte(*p, init_val); -#else /* RTE_MACHINE_CPUFLAG_SSE4_2 */ +#else /* RTE_MACHINE_CPUFLAG_SSE4_2 */ init_val = rte_jhash_1word(t, init_val); init_val = rte_jhash_1word(k->ip_src, init_val); init_val = rte_jhash_1word(k->ip_dst, init_val); @@ -219,23 +215,21 @@ onvm_ft_ipv4_hash_crc(const void *data, __rte_unused uint32_t data_len, /*software caculate RSS*/ static inline uint32_t -onvm_softrss(struct onvm_ft_ipv4_5tuple *key) -{ - union rte_thash_tuple tuple; - uint8_t rss_key_be[RTE_DIM(rss_symmetric_key)]; - uint32_t rss_l3l4; +onvm_softrss(struct onvm_ft_ipv4_5tuple *key) { + union rte_thash_tuple tuple; + uint8_t rss_key_be[RTE_DIM(rss_symmetric_key)]; + uint32_t rss_l3l4; - rte_convert_rss_key((uint32_t *)rss_symmetric_key, (uint32_t *)rss_key_be, - RTE_DIM(rss_symmetric_key)); + rte_convert_rss_key((uint32_t *)rss_symmetric_key, (uint32_t *)rss_key_be, RTE_DIM(rss_symmetric_key)); - tuple.v4.src_addr = rte_be_to_cpu_32(key->src_addr); - tuple.v4.dst_addr = rte_be_to_cpu_32(key->dst_addr); - tuple.v4.sport = rte_be_to_cpu_16(key->src_port); - tuple.v4.dport = rte_be_to_cpu_16(key->dst_port); + tuple.v4.src_addr = rte_be_to_cpu_32(key->src_addr); + tuple.v4.dst_addr = rte_be_to_cpu_32(key->dst_addr); + tuple.v4.sport = rte_be_to_cpu_16(key->src_port); + tuple.v4.dport = rte_be_to_cpu_16(key->dst_port); - rss_l3l4 = rte_softrss_be((uint32_t *)&tuple, RTE_THASH_V4_L4_LEN, rss_key_be); + rss_l3l4 = rte_softrss_be((uint32_t *)&tuple, RTE_THASH_V4_L4_LEN, rss_key_be); - return rss_l3l4; + return rss_l3l4; } #endif // _ONVM_FLOW_TABLE_H_ diff --git a/onvm/onvm_nflib/onvm_includes.h b/onvm/onvm_nflib/onvm_includes.h index d2fc15638..e2c6dedb1 100644 --- a/onvm/onvm_nflib/onvm_includes.h +++ b/onvm/onvm_nflib/onvm_includes.h @@ -5,9 +5,9 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside - * 2010-2014 Intel Corporation. All rights reserved. + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside + * 2010-2019 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,7 +38,6 @@ * ********************************************************************/ - /****************************************************************************** onvm_includes.h @@ -49,53 +48,46 @@ ******************************************************************************/ - #ifndef _ONVM_INCLUDES_H_ #define _ONVM_INCLUDES_H_ - /******************************Standard C library*****************************/ - +#include +#include +#include +#include #include #include -#include -#include #include -#include -#include #include -#include - +#include /********************************DPDK library*********************************/ - +#include +#include #include -#include -#include -#include +#include #include +#include +#include +#include #include -#include #include -#include -#include -#include #include -#include -#include #include -#include -#include +#include +#include +#include #include -#include +#include +#include #include - +#include /******************************Internal headers*******************************/ - #include "onvm_common.h" #endif // _ONVM_INCLUDES_H_ diff --git a/onvm/onvm_nflib/onvm_msg_common.h b/onvm/onvm_nflib/onvm_msg_common.h index 6f6226642..3895aa723 100644 --- a/onvm/onvm_nflib/onvm_msg_common.h +++ b/onvm/onvm_nflib/onvm_msg_common.h @@ -5,9 +5,9 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside - * 2010-2014 Intel Corporation + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside + * 2010-2019 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -40,8 +40,8 @@ between the manager and NFs ********************************************************************/ -#ifndef _MSG_COMMON_H_ -#define _MSG_COMMON_H_ +#ifndef _ONVM_MSG_COMMON_H_ +#define _ONVM_MSG_COMMON_H_ #include @@ -51,10 +51,12 @@ #define MSG_NF_STOPPING 3 #define MSG_NF_READY 4 #define MSG_SCALE 5 +#define MSG_FROM_NF 6 +#define MSG_REQUEST_LPM_REGION 7 struct onvm_nf_msg { uint8_t msg_type; /* Constant saying what type of message is */ - void *msg_data; /* These should be rte_malloc'd so they're stored in hugepages */ + void *msg_data; /* These should be rte_malloc'd so they're stored in hugepages */ }; -#endif +#endif // _ONVM_MSG_COMMON_H_ diff --git a/onvm/onvm_nflib/onvm_nflib.c b/onvm/onvm_nflib/onvm_nflib.c index 7c3aec830..6ee08db17 100644 --- a/onvm/onvm_nflib/onvm_nflib.c +++ b/onvm/onvm_nflib/onvm_nflib.c @@ -5,9 +5,9 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside - * 2016-2017 Hewlett Packard Enterprise Development LP + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside + * 2016-2019 Hewlett Packard Enterprise Development LP * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -48,10 +48,8 @@ ******************************************************************************/ - /***************************Standard C library********************************/ - #include #include @@ -60,15 +58,12 @@ /*****************************Internal headers********************************/ - -#include "onvm_nflib.h" #include "onvm_includes.h" +#include "onvm_nflib.h" #include "onvm_sc_common.h" - /**********************************Macros*************************************/ - // Possible NF packet consuming modes #define NF_MODE_UNKNOWN 0 #define NF_MODE_SINGLE 1 @@ -76,7 +71,6 @@ #define ONVM_NO_CALLBACK NULL - /******************************Global Variables*******************************/ // Shared data for host port information @@ -96,41 +90,38 @@ uint16_t **services; uint16_t *nf_per_service_count; // Shared pool for all NFs info -static struct rte_mempool *nf_info_mp; +static struct rte_mempool *nf_init_cfg_mp; // Shared pool for mgr <--> NF messages static struct rte_mempool *nf_msg_pool; -// True as long as the NF should keep processing packets -static uint8_t keep_running = 1; +// Global NF context to manage signal termination +static struct onvm_nf_local_ctx *main_nf_local_ctx; + +// Global NF specific signal handler +static handle_signal_func global_nf_signal_handler = NULL; // Shared data for default service chain struct onvm_service_chain *default_chain; -/***********************Internal Functions Prototypes*************************/ +/* Shared data for onvm config */ +struct onvm_configuration *onvm_config; +/* Flag to check if shared core mutex sleep/wakeup is enabled */ +uint8_t ONVM_NF_SHARE_CORES; -/* - * Function that initialize a NF info data structure. - * - * Input : the tag to name the NF - * Output : the data structure initialized - * - */ -static struct onvm_nf_info * -onvm_nflib_info_init(const char *tag); +/***********************Internal Functions Prototypes*************************/ /* * Function that initialize a nf tx info data structure. * - * Input : onvm_nf_info struct pointer + * Input : onvm_nf_init_cfg struct pointer * Output : the data structure initialized * */ -static void +static void onvm_nflib_nf_tx_mgr_init(struct onvm_nf *nf); - /* * Function printing an explanation of command line instruction for a NF. * @@ -140,7 +131,6 @@ onvm_nflib_nf_tx_mgr_init(struct onvm_nf *nf); static void onvm_nflib_usage(const char *progname); - /* * Function that parses the global arguments common to all NFs. * @@ -150,37 +140,36 @@ onvm_nflib_usage(const char *progname); * */ static int -onvm_nflib_parse_args(int argc, char *argv[], struct onvm_nf_info *nf_info); - - -/* -* Signal handler to catch SIGINT. -* -* Input : int corresponding to the signal catched -* -*/ -static void -onvm_nflib_handle_signal(int sig); +onvm_nflib_parse_args(int argc, char *argv[], struct onvm_nf_init_cfg *nf_init_cfg); /* * Check if there are packets in this NF's RX Queue and process them */ static inline uint16_t -onvm_nflib_dequeue_packets(void **pkts, struct onvm_nf *nf, pkt_handler_func handler) __attribute__((always_inline)); +onvm_nflib_dequeue_packets(void **pkts, struct onvm_nf_local_ctx *nf_local_ctx, + nf_pkt_handler_fn handler) __attribute__((always_inline)); /* * Check if there is a message available for this NF and process it */ static inline void -onvm_nflib_dequeue_messages(struct onvm_nf *nf) __attribute__((always_inline)); +onvm_nflib_dequeue_messages(struct onvm_nf_local_ctx *nf_local_ctx) __attribute__((always_inline)); /* - * Set this NF's status to not running and release memory + * Terminate the children spawned by the NF * * Input: Info struct corresponding to this NF */ static void -onvm_nflib_cleanup(struct onvm_nf_info *nf_info); +onvm_nflib_terminate_children(struct onvm_nf *nf); + +/* + * Set this NF's status to not running and release memory + * + * Input: pointer to context struct for this NF + */ +static void +onvm_nflib_cleanup(struct onvm_nf_local_ctx *nf_local_ctx); /* * Entry point of a spawned child NF @@ -195,7 +184,7 @@ static int onvm_nflib_is_scale_info_valid(struct onvm_nf_scale_info *scale_info); /* - * Initialize dpdk as a secondary proc + * Initialize dpdk as a secondary proc * * Input: arc, argv args */ @@ -210,184 +199,118 @@ static int onvm_nflib_lookup_shared_structs(void); /* - * Start the NF by signaling manager that its ready to recieve packets + * Parse the custom onvm config shared with manager * - * Input: Info struct corresponding to this NF */ -static int -onvm_nflib_start_nf(struct onvm_nf_info *nf_info); +static void +onvm_nflib_parse_config(struct onvm_configuration *onvm_config); /* * Entry point of the NF main loop * - * Input: void pointer, points to the onvm_nf struct + * Input: void pointer, points to the onvm_nf_local_ctx struct */ void * onvm_nflib_thread_main_loop(void *arg); -/************************************API**************************************/ - - -static int -onvm_nflib_dpdk_init(int argc, char *argv[]) { - int retval_eal = 0; - if ((retval_eal = rte_eal_init(argc, argv)) < 0) - return -1; - return retval_eal; -} - -static int -onvm_nflib_lookup_shared_structs(void) { - const struct rte_memzone *mz_nf; - const struct rte_memzone *mz_port; - const struct rte_memzone *mz_cores; - const struct rte_memzone *mz_scp; - const struct rte_memzone *mz_services; - const struct rte_memzone *mz_nf_per_service; - struct rte_mempool *mp; - struct onvm_service_chain **scp; - - /* Lookup mempool for nf_info struct */ - nf_info_mp = rte_mempool_lookup(_NF_MEMPOOL_NAME); - if (nf_info_mp == NULL) - rte_exit(EXIT_FAILURE, "No NF Info mempool - bye\n"); - - /* Lookup mempool for NF messages */ - nf_msg_pool = rte_mempool_lookup(_NF_MSG_POOL_NAME); - if (nf_msg_pool == NULL) - rte_exit(EXIT_FAILURE, "No NF Message mempool - bye\n"); +/* + * Function to initalize the shared core support + * + * Input : Number of NF instances + */ +static void +init_shared_core_mode_info(uint16_t instance_id); - mp = rte_mempool_lookup(PKTMBUF_POOL_NAME); - if (mp == NULL) - rte_exit(EXIT_FAILURE, "Cannot get mempool for mbufs\n"); +/* + * Signal handler to catch SIGINT/SIGTERM. + * + * Input : int corresponding to the signal catched + * + */ +void +onvm_nflib_handle_signal(int signal); - /* Lookup mempool for NF structs */ - mz_nf = rte_memzone_lookup(MZ_NF_INFO); - if (mz_nf == NULL) - rte_exit(EXIT_FAILURE, "Cannot get NF structure mempool\n"); - nfs = mz_nf->addr; +/************************************API**************************************/ - mz_services = rte_memzone_lookup(MZ_SERVICES_INFO); - if (mz_services == NULL) { - rte_exit(EXIT_FAILURE, "Cannot get service information\n"); - } - services = mz_services->addr; +struct onvm_nf_local_ctx * +onvm_nflib_init_nf_local_ctx(void) { + struct onvm_nf_local_ctx *nf_local_ctx; - mz_nf_per_service = rte_memzone_lookup(MZ_NF_PER_SERVICE_INFO); - if (mz_nf_per_service == NULL) { - rte_exit(EXIT_FAILURE, "Cannot get NF per service information\n"); - } - nf_per_service_count = mz_nf_per_service->addr; + nf_local_ctx = (struct onvm_nf_local_ctx*)calloc(1, sizeof(struct onvm_nf_local_ctx)); + if (nf_local_ctx == NULL) + rte_exit(EXIT_FAILURE, "Failed to allocate memory for NF context\n"); - mz_port = rte_memzone_lookup(MZ_PORT_INFO); - if (mz_port == NULL) - rte_exit(EXIT_FAILURE, "Cannot get port info structure\n"); - ports = mz_port->addr; + rte_atomic16_init(&nf_local_ctx->keep_running); + rte_atomic16_set(&nf_local_ctx->keep_running, 1); + rte_atomic16_init(&nf_local_ctx->nf_init_finished); + rte_atomic16_set(&nf_local_ctx->nf_init_finished, 0); + rte_atomic16_init(&nf_local_ctx->nf_stopped); + rte_atomic16_set(&nf_local_ctx->nf_stopped, 0); - mz_cores = rte_memzone_lookup(MZ_CORES_STATUS); - if (mz_cores == NULL) - rte_exit(EXIT_FAILURE, "Cannot get core status structure\n"); - cores = mz_cores->addr; + return nf_local_ctx; +} - mz_scp = rte_memzone_lookup(MZ_SCP_INFO); - if (mz_scp == NULL) - rte_exit(EXIT_FAILURE, "Cannot get service chain info structre\n"); - scp = mz_scp->addr; - default_chain = *scp; - onvm_sc_print(default_chain); +struct onvm_nf_function_table * +onvm_nflib_init_nf_function_table(void) { + struct onvm_nf_function_table *nf_function_table; - mgr_msg_queue = rte_ring_lookup(_MGR_MSG_QUEUE_NAME); - if (mgr_msg_queue == NULL) - rte_exit(EXIT_FAILURE, "Cannot get nf_info ring"); + nf_function_table = (struct onvm_nf_function_table*)calloc(1, sizeof(struct onvm_nf_function_table)); + if (nf_function_table == NULL) + rte_exit(EXIT_FAILURE, "Failed to allocate memory for NF context\n"); - return 0; + return nf_function_table; } -static int -onvm_nflib_start_nf(struct onvm_nf_info *nf_info) { - struct onvm_nf_msg *startup_msg; +int +onvm_nflib_request_lpm(struct lpm_request *lpm_req) { + struct onvm_nf_msg *request_message; + int ret; - /* Put this NF's info struct onto queue for manager to process startup */ - if (rte_mempool_get(nf_msg_pool, (void**)(&startup_msg)) != 0) { - rte_mempool_put(nf_info_mp, nf_info); // give back memory - rte_exit(EXIT_FAILURE, "Cannot create startup msg"); - } + ret = rte_mempool_get(nf_msg_pool, (void **) (&request_message)); + if (ret != 0) return ret; - /* Tell the manager we're ready to recieve packets */ - startup_msg->msg_type = MSG_NF_STARTING; - startup_msg->msg_data = nf_info; - if (rte_ring_enqueue(mgr_msg_queue, startup_msg) < 0) { - rte_mempool_put(nf_info_mp, nf_info); // give back mermory - rte_mempool_put(nf_msg_pool, startup_msg); - rte_exit(EXIT_FAILURE, "Cannot send nf_info to manager"); + request_message->msg_type = MSG_REQUEST_LPM_REGION; + request_message->msg_data = lpm_req; + + ret = rte_ring_enqueue(mgr_msg_queue, request_message); + if (ret < 0) { + rte_mempool_put(nf_msg_pool, request_message); + return ret; } - /* Wait for a NF id to be assigned by the manager */ - RTE_LOG(INFO, APP, "Waiting for manager to assign an ID...\n"); - for (; nf_info->status == (uint16_t)NF_WAITING_FOR_ID ;) { + lpm_req->status = NF_WAITING_FOR_LPM; + for (; lpm_req->status == (uint16_t) NF_WAITING_FOR_LPM;) { sleep(1); } - /* This NF is trying to declare an ID already in use. */ - if (nf_info->status == NF_ID_CONFLICT) { - rte_mempool_put(nf_info_mp, nf_info); - rte_exit(NF_ID_CONFLICT, "Selected ID already in use. Exiting...\n"); - } else if (nf_info->status == NF_SERVICE_MAX) { - rte_mempool_put(nf_info_mp, nf_info); - rte_exit(NF_SERVICE_MAX, "Service ID must be less than %d\n", MAX_SERVICES); - } else if (nf_info->status == NF_SERVICE_COUNT_MAX) { - rte_mempool_put(nf_info_mp, nf_info); - rte_exit(NF_SERVICE_COUNT_MAX, "Maximum amount of NF's per service spawned, must be less than %d", MAX_NFS_PER_SERVICE); - } else if(nf_info->status == NF_NO_IDS) { - rte_mempool_put(nf_info_mp, nf_info); - rte_exit(NF_NO_IDS, "There are no ids available for this NF\n"); - } else if(nf_info->status == NF_NO_CORES) { - rte_mempool_put(nf_info_mp, nf_info); - rte_exit(NF_NO_IDS, "There are no cores available for this NF\n"); - } else if(nf_info->status == NF_NO_DEDICATED_CORES) { - rte_mempool_put(nf_info_mp, nf_info); - rte_exit(NF_NO_IDS, "There is no space to assign a dedicated core, " - "or manually selected core has NFs running\n"); - } else if(nf_info->status == NF_CORE_OUT_OF_RANGE) { - rte_mempool_put(nf_info_mp, nf_info); - rte_exit(NF_NO_IDS, "Requested core is not enabled or not in range\n"); - } else if(nf_info->status == NF_CORE_BUSY) { - rte_mempool_put(nf_info_mp, nf_info); - rte_exit(NF_NO_IDS, "Requested core is busy\n"); - } else if(nf_info->status != NF_STARTING) { - rte_mempool_put(nf_info_mp, nf_info); - rte_exit(EXIT_FAILURE, "Error occurred during manager initialization\n"); - } - - /* Set mode to UNKNOWN, to be determined later */ - nfs[nf_info->instance_id].nf_mode = NF_MODE_UNKNOWN; - - /* Initialize empty NF's tx manager */ - onvm_nflib_nf_tx_mgr_init(&nfs[nf_info->instance_id]); - - /* Set the parent id to none */ - nfs[nf_info->instance_id].parent = 0; + rte_mempool_put(nf_msg_pool, request_message); + return lpm_req->status; +} - RTE_LOG(INFO, APP, "Using Instance ID %d\n", nf_info->instance_id); - RTE_LOG(INFO, APP, "Using Service ID %d\n", nf_info->service_id); - RTE_LOG(INFO, APP, "Running on core %d\n", nf_info->core); +int +onvm_nflib_start_signal_handler(struct onvm_nf_local_ctx *nf_local_ctx, handle_signal_func nf_signal_handler) { + /* Signal handling is global thus save global context */ + main_nf_local_ctx = nf_local_ctx; + global_nf_signal_handler = nf_signal_handler; - RTE_LOG(INFO, APP, "Finished Process Init.\n"); + printf("[Press Ctrl-C to quit ...]\n"); + signal(SIGINT, onvm_nflib_handle_signal); + signal(SIGTERM, onvm_nflib_handle_signal); return 0; } int -onvm_nflib_init(int argc, char *argv[], const char *nf_tag, struct onvm_nf_info **nf_info_p) { - int retval_parse, retval_final; - struct onvm_nf_info *nf_info; - int retval_eal = 0; +onvm_nflib_init(int argc, char *argv[], const char *nf_tag, struct onvm_nf_local_ctx *nf_local_ctx, + struct onvm_nf_function_table *nf_function_table) { + struct onvm_nf_init_cfg *nf_init_cfg; + int ret, retval_eal, retval_parse, retval_final; int use_config = 0; /* Check to see if a config file should be used */ if (strcmp(argv[1], "-F") == 0) { use_config = 1; - cJSON* config = onvm_config_parse_file(argv[2]); + cJSON *config = onvm_config_parse_file(argv[2]); if (config == NULL) { printf("Could not parse config file\n"); return -1; @@ -408,23 +331,25 @@ onvm_nflib_init(int argc, char *argv[], const char *nf_tag, struct onvm_nf_info return retval_eal; /* Modify argc and argv to conform to getopt rules for parse_nflib_args */ - argc -= retval_eal; argv += retval_eal; + argc -= retval_eal; + argv += retval_eal; /* Reset getopt global variables opterr and optind to their default values */ - opterr = 0; optind = 1; + opterr = 0; + optind = 1; - /* Lookup the info shared or created by the manager */ + /* Lookup the info shared or created by the manager */ onvm_nflib_lookup_shared_structs(); /* Initialize the info struct */ - nf_info = onvm_nflib_info_init(nf_tag); - *nf_info_p = nf_info; + nf_init_cfg = onvm_nflib_init_nf_init_cfg(nf_tag); - if ((retval_parse = onvm_nflib_parse_args(argc, argv, nf_info)) < 0) + if ((retval_parse = onvm_nflib_parse_args(argc, argv, nf_init_cfg)) < 0) rte_exit(EXIT_FAILURE, "Invalid command-line arguments\n"); /* Reset getopt global variables opterr and optind to their default values */ - opterr = 0; optind = 1; + opterr = 0; + optind = 1; /* * Calculate the offset that the nf will use to modify argc and argv for its @@ -436,11 +361,13 @@ onvm_nflib_init(int argc, char *argv[], const char *nf_tag, struct onvm_nf_info */ retval_final = (retval_eal + retval_parse) - 1; - onvm_nflib_start_nf(nf_info); + if ((ret = onvm_nflib_start_nf(nf_local_ctx, nf_init_cfg)) < 0) + return ret; - keep_running = 1; + /* Save the nf specifc function table */ + nf_local_ctx->nf->function_table = nf_function_table; - //Set to 3 because that is the bare minimum number of arguments, the config file will increase this number + // Set to 3 because that is the bare minimum number of arguments, the config file will increase this number if (use_config) { return 3; } @@ -448,34 +375,158 @@ onvm_nflib_init(int argc, char *argv[], const char *nf_tag, struct onvm_nf_info return retval_final; } - int -onvm_nflib_run_callback( - struct onvm_nf_info* info, - pkt_handler_func handler, - callback_handler_func callback) -{ - struct onvm_nf * nf; - - /* Listen for ^C and docker stop so we can exit gracefully */ - signal(SIGINT, onvm_nflib_handle_signal); - signal(SIGTERM, onvm_nflib_handle_signal); +onvm_nflib_start_nf(struct onvm_nf_local_ctx *nf_local_ctx, struct onvm_nf_init_cfg *nf_init_cfg) { + struct onvm_nf_msg *startup_msg; + struct onvm_nf *nf; + int i; + + /* Block signals, ensure only the parent signal handler gets the signal */ + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGTERM); + if (pthread_sigmask(SIG_BLOCK, &mask, NULL) != 0) { + printf("Could not set pthread sigmask\n"); + return -1; + } + if (!rte_atomic16_read(&nf_local_ctx->keep_running)) { + return ONVM_SIGNAL_TERMINATION; + } + + /* Put this NF's info struct onto queue for manager to process startup */ + if (rte_mempool_get(nf_msg_pool, (void **)(&startup_msg)) != 0) { + rte_mempool_put(nf_init_cfg_mp, nf_init_cfg); // give back memory + rte_exit(EXIT_FAILURE, "Cannot create startup msg"); + } + + /* Tell the manager we're ready to recieve packets */ + startup_msg->msg_type = MSG_NF_STARTING; + startup_msg->msg_data = nf_init_cfg; + if (rte_ring_enqueue(mgr_msg_queue, startup_msg) < 0) { + rte_mempool_put(nf_init_cfg_mp, nf_init_cfg); // give back mermory + rte_mempool_put(nf_msg_pool, startup_msg); + rte_exit(EXIT_FAILURE, "Cannot send nf_init_cfg to manager"); + } - nf = &nfs[info->instance_id]; + /* Wait for a NF id to be assigned by the manager */ + RTE_LOG(INFO, APP, "Waiting for manager to assign an ID...\n"); + for (; nf_init_cfg->status == (uint16_t)NF_WAITING_FOR_ID;) { + sleep(1); + if (!rte_atomic16_read(&nf_local_ctx->keep_running)) { + /* Wait because we sent a message to the onvm_mgr */ + for (i = 0; i < NF_TERM_INIT_ITER_TIMES && nf_init_cfg->status != NF_STARTING; i++) { + printf("Waiting for onvm_mgr to recieve the message before shutting down\n"); + sleep(NF_TERM_WAIT_TIME); + } + /* Mark init as finished, even though we're exiting onvm_nflib_stop will do proper cleanup */ + if (nf_init_cfg->status == NF_STARTING) { + nf_local_ctx->nf = &nfs[nf_init_cfg->instance_id]; + rte_atomic16_set(&nf_local_ctx->nf_init_finished, 1); + } + return ONVM_SIGNAL_TERMINATION; + } + } - /* Don't allow conflicting NF modes */ - if (nf->nf_mode == NF_MODE_RING) { + /* This NF is trying to declare an ID already in use. */ + if (nf_init_cfg->status == NF_ID_CONFLICT) { + rte_mempool_put(nf_init_cfg_mp, nf_init_cfg); + printf("%s", "Selected ID already in use. Exiting...\n"); + return -NF_ID_CONFLICT; + } else if (nf_init_cfg->status == NF_SERVICE_MAX) { + rte_mempool_put(nf_init_cfg_mp, nf_init_cfg); + printf("Service ID must be less than %d\n", MAX_SERVICES); + return -NF_SERVICE_MAX; + } else if (nf_init_cfg->status == NF_SERVICE_COUNT_MAX) { + rte_mempool_put(nf_init_cfg_mp, nf_init_cfg); + printf("Maximum amount of NF's per service spawned, must be less than %d", MAX_NFS_PER_SERVICE); + return -NF_SERVICE_COUNT_MAX; + } else if (nf_init_cfg->status == NF_NO_IDS) { + rte_mempool_put(nf_init_cfg_mp, nf_init_cfg); + printf("There are no ids available for this NF\n"); + return -NF_NO_IDS; + } else if (nf_init_cfg->status == NF_NO_CORES) { + rte_mempool_put(nf_init_cfg_mp, nf_init_cfg); + printf("There are no cores available for this NF\n"); + return -NF_NO_CORES; + } else if (nf_init_cfg->status == NF_NO_DEDICATED_CORES) { + rte_mempool_put(nf_init_cfg_mp, nf_init_cfg); + printf("There is no space to assign a dedicated core, or the selected core has NFs running\n"); + return -NF_NO_DEDICATED_CORES; + } else if (nf_init_cfg->status == NF_CORE_OUT_OF_RANGE) { + rte_mempool_put(nf_init_cfg_mp, nf_init_cfg); + printf("Requested core is not enabled or not in range\n"); + return -NF_CORE_OUT_OF_RANGE; + } else if (nf_init_cfg->status == NF_CORE_BUSY) { + rte_mempool_put(nf_init_cfg_mp, nf_init_cfg); + printf("Requested core is busy\n"); + return -NF_CORE_BUSY; + } else if (nf_init_cfg->status != NF_STARTING) { + rte_mempool_put(nf_init_cfg_mp, nf_init_cfg); + printf("Error occurred during manager initialization\n"); return -1; } - nf->nf_mode = NF_MODE_SINGLE; - /* Save the nf specifc functions, can be used if NFs spawn new threads */ - nf->nf_pkt_function = handler; - nf->nf_callback_function = callback; + nf = &nfs[nf_init_cfg->instance_id]; + + /* Mark init as finished, sig handler/onvm_nflib_stop will now do proper cleanup */ + if (rte_atomic16_read(&nf_local_ctx->nf_init_finished) == 0) { + nf_local_ctx->nf = nf; + rte_atomic16_set(&nf_local_ctx->nf_init_finished, 1); + } + + /* Init finished free the bootstrap struct */ + rte_mempool_put(nf_init_cfg_mp, nf_init_cfg); + + /* Initialize empty NF's tx manager */ + onvm_nflib_nf_tx_mgr_init(nf); + + /* Set the parent id to none */ + nf->thread_info.parent = 0; + rte_atomic16_init(&nf->thread_info.children_cnt); + rte_atomic16_set(&nf->thread_info.children_cnt, 0); + + /* In case this instance_id is reused, clear all function pointers */ + nf->function_table = NULL; + + if (ONVM_NF_SHARE_CORES) { + RTE_LOG(INFO, APP, "Shared CPU support enabled\n"); + init_shared_core_mode_info(nf->instance_id); + } + + RTE_LOG(INFO, APP, "Using Instance ID %d\n", nf->instance_id); + RTE_LOG(INFO, APP, "Using Service ID %d\n", nf->service_id); + RTE_LOG(INFO, APP, "Running on core %d\n", nf->thread_info.core); + + if (nf->flags.time_to_live) + RTE_LOG(INFO, APP, "Time to live set to %u\n", nf->flags.time_to_live); + if (nf->flags.pkt_limit) + RTE_LOG(INFO, APP, "Packet limit (rx) set to %u\n", nf->flags.pkt_limit); + + /* + * Allow this for cases when there is not enough cores and using + * the shared core mode is not an option + */ + if (ONVM_CHECK_BIT(nf->flags.init_options, SHARE_CORE_BIT) && !ONVM_NF_SHARE_CORES) + RTE_LOG(WARNING, APP, "Requested shared core allocation but shared core mode is NOT " + "enabled, this will hurt performance, proceed with caution\n"); + + RTE_LOG(INFO, APP, "Finished Process Init.\n"); + + return 0; +} + +int +onvm_nflib_run(struct onvm_nf_local_ctx *nf_local_ctx) { + int ret; pthread_t main_loop_thread; - pthread_create(&main_loop_thread, NULL, onvm_nflib_thread_main_loop, (void *)nf); - pthread_join(main_loop_thread, NULL); + if ((ret = pthread_create(&main_loop_thread, NULL, onvm_nflib_thread_main_loop, (void *)nf_local_ctx)) < 0) { + rte_exit(EXIT_FAILURE, "Failed to spawn main loop thread, error %d", ret); + } + if ((ret = pthread_join(main_loop_thread, NULL)) < 0) { + rte_exit(EXIT_FAILURE, "Failed to join with main loop thread, error %d", ret); + } return 0; } @@ -483,31 +534,29 @@ onvm_nflib_run_callback( void * onvm_nflib_thread_main_loop(void *arg) { struct rte_mbuf *pkts[PACKET_READ_SIZE]; - struct onvm_nf * nf; - uint16_t nb_pkts_added, i; - struct onvm_nf_info* info; - pkt_handler_func handler; - callback_handler_func callback; + struct onvm_nf_local_ctx *nf_local_ctx; + struct onvm_nf *nf; + uint16_t nb_pkts_added; + uint64_t start_time; int ret; - nf = (struct onvm_nf *)arg; - onvm_threading_core_affinitize(nf->info->core); - - info = nf->info; - handler = nf->nf_pkt_function; - callback = nf->nf_callback_function; - - /* Runs the NF setup function */ - if (nf->nf_setup_function != NULL) - nf->nf_setup_function(info); + nf_local_ctx = (struct onvm_nf_local_ctx *)arg; + nf = nf_local_ctx->nf; + onvm_threading_core_affinitize(nf->thread_info.core); printf("Sending NF_READY message to manager...\n"); - ret = onvm_nflib_nf_ready(info); - if (ret != 0) rte_exit(EXIT_FAILURE, "Unable to message manager\n"); + ret = onvm_nflib_nf_ready(nf); + if (ret != 0) + rte_exit(EXIT_FAILURE, "Unable to message manager\n"); - printf("[Press Ctrl-C to quit ...]\n"); - for (; keep_running;) { - nb_pkts_added = onvm_nflib_dequeue_packets((void **) pkts, nf, handler); + /* Run the setup function (this might send pkts so done after the state change) */ + if (nf->function_table->setup != NULL) + nf->function_table->setup(nf_local_ctx); + + start_time = rte_get_tsc_cycles(); + for (;rte_atomic16_read(&nf_local_ctx->keep_running) && rte_atomic16_read(&main_nf_local_ctx->keep_running);) { + nb_pkts_added = + onvm_nflib_dequeue_packets((void **)pkts, nf_local_ctx, nf->function_table->pkt_handler); if (likely(nb_pkts_added > 0)) { onvm_pkt_process_tx_batch(nf->nf_tx_mgr, pkts, nb_pkts_added, nf); @@ -517,142 +566,136 @@ onvm_nflib_thread_main_loop(void *arg) { onvm_pkt_enqueue_tx_thread(nf->nf_tx_mgr->to_tx_buf, nf); onvm_pkt_flush_all_nfs(nf->nf_tx_mgr, nf); - onvm_nflib_dequeue_messages(nf); - if (callback != ONVM_NO_CALLBACK) { - keep_running = !(*callback)(nf->info) && keep_running; + onvm_nflib_dequeue_messages(nf_local_ctx); + if (nf->function_table->user_actions != ONVM_NO_CALLBACK) { + rte_atomic16_set(&nf_local_ctx->keep_running, + !(*nf->function_table->user_actions)(nf_local_ctx) && + rte_atomic16_read(&nf_local_ctx->keep_running)); } - } - /* Wait for children to quit */ - for (i = 0; i < MAX_NFS; i++) - while (nfs[i].parent == nf->instance_id && nfs[i].info != NULL) { - sleep(1); + if (nf->flags.time_to_live && unlikely((rte_get_tsc_cycles() - start_time) * + TIME_TTL_MULTIPLIER / rte_get_timer_hz() >= nf->flags.time_to_live)) { + printf("Time to live exceeded, shutting down\n"); + rte_atomic16_set(&nf_local_ctx->keep_running, 0); } - - /* Stop and free */ - onvm_nflib_cleanup(info); - + if (nf->flags.pkt_limit && unlikely(nf->stats.rx >= (uint64_t)nf->flags.pkt_limit * + PKT_TTL_MULTIPLIER)) { + printf("Packet limit exceeded, shutting down\n"); + rte_atomic16_set(&nf_local_ctx->keep_running, 0); + } + } return NULL; } int -onvm_nflib_run(struct onvm_nf_info* info, pkt_handler_func handler) { - return onvm_nflib_run_callback(info, handler, ONVM_NO_CALLBACK); -} - -int -onvm_nflib_return_pkt(struct onvm_nf_info* nf_info, struct rte_mbuf* pkt) { - return onvm_nflib_return_pkt_bulk(nf_info, &pkt, 1); +onvm_nflib_return_pkt(struct onvm_nf *nf, struct rte_mbuf *pkt) { + return onvm_nflib_return_pkt_bulk(nf, &pkt, 1); } int -onvm_nflib_return_pkt_bulk(struct onvm_nf_info *nf_info, struct rte_mbuf** pkts, uint16_t count) { +onvm_nflib_return_pkt_bulk(struct onvm_nf *nf, struct rte_mbuf **pkts, uint16_t count) { unsigned int i; - if (pkts == NULL || count == 0) return -1; - if (unlikely(rte_ring_enqueue_bulk(nfs[nf_info->instance_id].tx_q, (void **)pkts, count, NULL) == 0)) { - nfs[nf_info->instance_id].stats.tx_drop += count; + if (pkts == NULL || count == 0) + return -1; + if (unlikely(rte_ring_enqueue_bulk(nf->tx_q, (void **)pkts, count, NULL) == 0)) { + nf->stats.tx_drop += count; for (i = 0; i < count; i++) { rte_pktmbuf_free(pkts[i]); } return -ENOBUFS; + } else { + nf->stats.tx_returned += count; } - else nfs[nf_info->instance_id].stats.tx_returned += count; + return 0; } int -onvm_nflib_nf_ready(struct onvm_nf_info *info) { +onvm_nflib_nf_ready(struct onvm_nf *nf) { struct onvm_nf_msg *startup_msg; int ret; /* Put this NF's info struct onto queue for manager to process startup */ - ret = rte_mempool_get(nf_msg_pool, (void**)(&startup_msg)); - if (ret != 0) return ret; + ret = rte_mempool_get(nf_msg_pool, (void **)(&startup_msg)); + if (ret != 0) + return ret; startup_msg->msg_type = MSG_NF_READY; - startup_msg->msg_data = info; + startup_msg->msg_data = nf; ret = rte_ring_enqueue(mgr_msg_queue, startup_msg); if (ret < 0) { rte_mempool_put(nf_msg_pool, startup_msg); return ret; } - return 0; -} -int -onvm_nflib_handle_msg(struct onvm_nf_msg *msg, __attribute__((unused)) struct onvm_nf_info *info) { - switch(msg->msg_type) { - case MSG_STOP: - RTE_LOG(INFO, APP, "Shutting down...\n"); - keep_running = 0; - break; - case MSG_SCALE: - RTE_LOG(INFO, APP, "Received scale message...\n"); - onvm_nflib_scale((struct onvm_nf_scale_info*)msg->msg_data); - break; - case MSG_NOOP: - default: - break; + /* Don't start running before the onvm_mgr handshake is finished */ + while (nf->status != NF_RUNNING) { + sleep(1); } return 0; } -void -onvm_nflib_stop(struct onvm_nf_info *nf_info) { - onvm_nflib_cleanup(nf_info); -} - - -struct rte_ring * -onvm_nflib_get_tx_ring(struct onvm_nf_info* info) { - if (info == NULL) { - return NULL; - } - - /* Don't allow conflicting NF modes */ - if (nfs[info->instance_id].nf_mode == NF_MODE_SINGLE) { - return NULL; +int +onvm_nflib_handle_msg(struct onvm_nf_msg *msg, struct onvm_nf_local_ctx *nf_local_ctx) { + switch (msg->msg_type) { + case MSG_STOP: + RTE_LOG(INFO, APP, "Shutting down...\n"); + rte_atomic16_set(&nf_local_ctx->keep_running, 0); + break; + case MSG_SCALE: + RTE_LOG(INFO, APP, "Received scale message...\n"); + onvm_nflib_scale((struct onvm_nf_scale_info*)msg->msg_data); + break; + case MSG_FROM_NF: + RTE_LOG(INFO, APP, "Recieved MSG from other NF"); + if (nf_local_ctx->nf->function_table->msg_handler != NULL) { + nf_local_ctx->nf->function_table->msg_handler(msg->msg_data, nf_local_ctx); + } + break; + case MSG_NOOP: + default: + break; } - /* We should return the tx_ring associated with the info struct */ - nfs[info->instance_id].nf_mode = NF_MODE_RING; - return (struct rte_ring *)(&(nfs[info->instance_id].tx_q)); + return 0; } +int +onvm_nflib_send_msg_to_nf(uint16_t dest, void *msg_data) { + int ret; + struct onvm_nf_msg *msg; -struct rte_ring * -onvm_nflib_get_rx_ring(struct onvm_nf_info* info) { - if (info == NULL) { - return NULL; + ret = rte_mempool_get(nf_msg_pool, (void**)(&msg)); + if (ret != 0) { + RTE_LOG(INFO, APP, "Oh the huge manatee! Unable to allocate msg from pool :(\n"); + return ret; } - /* Don't allow conflicting NF modes */ - if (nfs[info->instance_id].nf_mode == NF_MODE_SINGLE) { - return NULL; - } + msg->msg_type = MSG_FROM_NF; + msg->msg_data = msg_data; - /* We should return the rx_ring associated with the info struct */ - nfs[info->instance_id].nf_mode = NF_MODE_RING; - return (struct rte_ring *)(&(nfs[info->instance_id].rx_q)); + return rte_ring_enqueue(nfs[dest].msg_q, (void*)msg); } - -struct onvm_nf * -onvm_nflib_get_nf(uint16_t id) { - /* Don't allow conflicting NF modes */ - if (nfs[id].nf_mode == NF_MODE_SINGLE) { - return NULL; +void +onvm_nflib_stop(struct onvm_nf_local_ctx *nf_local_ctx) { + if (nf_local_ctx == NULL || nf_local_ctx->nf == NULL || rte_atomic16_read(&nf_local_ctx->nf_stopped) != 0) { + return; } - /* We should return the NF struct referenced by instance id */ - nfs[id].nf_mode = NF_MODE_RING; - return &nfs[id]; + /* Ensure we only call nflib_stop once */ + rte_atomic16_set(&nf_local_ctx->nf_stopped, 1); + + /* Terminate children */ + onvm_nflib_terminate_children(nf_local_ctx->nf); + /* Stop and free */ + onvm_nflib_cleanup(nf_local_ctx); } -void -onvm_nflib_set_setup_function(struct onvm_nf_info *info, setup_func setup) { - nfs[info->instance_id].nf_setup_function = setup; +struct onvm_configuration * +onvm_nflib_get_onvm_config(void) { + return onvm_config; } int @@ -665,58 +708,95 @@ onvm_nflib_scale(struct onvm_nf_scale_info *scale_info) { return -1; } + rte_atomic16_inc(&nfs[scale_info->parent->instance_id].thread_info.children_cnt); + + /* Careful, this is required for shared core scaling TODO: resolve */ + if (ONVM_NF_SHARE_CORES) + sleep(1); + ret = pthread_create(&app_thread, NULL, &onvm_nflib_start_child, scale_info); if (ret < 0) { - RTE_LOG(INFO, APP, "Failed to create thread\n"); + rte_atomic16_dec(&nfs[scale_info->parent->instance_id].thread_info.children_cnt); + RTE_LOG(INFO, APP, "Failed to create child thread\n"); return -1; } ret = pthread_detach(app_thread); if (ret < 0) { - RTE_LOG(INFO, APP, "Failed to detach thread\n"); + RTE_LOG(INFO, APP, "Failed to detach child thread\n"); return -1; } return 0; } +struct onvm_nf_init_cfg * +onvm_nflib_init_nf_init_cfg(const char *tag) { + void *mempool_data; + struct onvm_nf_init_cfg *nf_init_cfg; + + if (rte_mempool_get(nf_init_cfg_mp, &mempool_data) < 0) { + rte_exit(EXIT_FAILURE, "Failed to get nf nf_init_cfg memory\n"); + } + + if (mempool_data == NULL) { + rte_exit(EXIT_FAILURE, "Client Info struct not allocated\n"); + } + + nf_init_cfg = (struct onvm_nf_init_cfg *)mempool_data; + nf_init_cfg->instance_id = NF_NO_ID; + nf_init_cfg->core = rte_lcore_id(); + nf_init_cfg->init_options = 0; + nf_init_cfg->status = NF_WAITING_FOR_ID; + + /* Allocate memory for the tag so that onvm_mgr can access it */ + nf_init_cfg->tag = rte_malloc("nf_tag", TAG_SIZE, 0); + strncpy(nf_init_cfg->tag, tag, TAG_SIZE); + /* In case provided tag was longer than TAG_SIZE */ + nf_init_cfg->tag[TAG_SIZE - 1] = '\0'; + + /* TTL and packet limit disabled by default */ + nf_init_cfg->time_to_live = 0; + nf_init_cfg->pkt_limit = 0; + + return nf_init_cfg; +} + +struct onvm_nf_init_cfg * +onvm_nflib_inherit_parent_init_cfg(struct onvm_nf *parent) { + struct onvm_nf_init_cfg *nf_init_cfg; + + nf_init_cfg = onvm_nflib_init_nf_init_cfg(parent->tag); + + nf_init_cfg->service_id = parent->service_id; + nf_init_cfg->core = parent->thread_info.core; + nf_init_cfg->init_options = parent->flags.init_options; + nf_init_cfg->time_to_live = parent->flags.time_to_live; + nf_init_cfg->pkt_limit = parent->flags.pkt_limit; + + return nf_init_cfg; +} + struct onvm_nf_scale_info * -onvm_nflib_get_empty_scaling_config(struct onvm_nf_info *parent_info) { +onvm_nflib_get_empty_scaling_config(struct onvm_nf *parent) { struct onvm_nf_scale_info *scale_info; scale_info = rte_calloc("nf_scale_info", 1, sizeof(struct onvm_nf_scale_info), 0); - scale_info->parent = parent_info; - scale_info->instance_id = NF_NO_ID; - scale_info->flags = 0; + scale_info->nf_init_cfg = onvm_nflib_init_nf_init_cfg(parent->tag); + scale_info->parent = parent; return scale_info; } struct onvm_nf_scale_info * -onvm_nflib_inherit_parent_config(struct onvm_nf_info *parent_info, void *data) { +onvm_nflib_inherit_parent_config(struct onvm_nf *parent, void *data) { struct onvm_nf_scale_info *scale_info; - struct onvm_nf *parent_nf; - parent_nf = &nfs[parent_info->instance_id]; scale_info = rte_calloc("nf_scale_info", 1, sizeof(struct onvm_nf_scale_info), 0); - scale_info->parent = parent_info; - scale_info->instance_id = NF_NO_ID; - scale_info->service_id = parent_info->service_id; - scale_info->tag = parent_info->tag; - scale_info->core = parent_info->core; - scale_info->flags = parent_info->flags; + scale_info->nf_init_cfg = onvm_nflib_inherit_parent_init_cfg(parent); + scale_info->parent = parent; scale_info->data = data; - if (parent_nf->nf_mode == NF_MODE_SINGLE) { - scale_info->pkt_func = parent_nf->nf_pkt_function; - scale_info->setup_func = parent_nf->nf_setup_function; - scale_info->callback_func = parent_nf->nf_callback_function; - } else if (parent_nf->nf_mode == NF_MODE_RING) { - scale_info->setup_func = parent_nf->nf_setup_function; - scale_info->adv_rings_func = parent_nf->nf_advanced_rings_function; - } else { - RTE_LOG(INFO, APP, "Unknown NF mode detected\n"); - return NULL; - } + scale_info->function_table = parent->function_table; return scale_info; } @@ -728,19 +808,112 @@ onvm_nflib_get_default_chain(void) { /******************************Helper functions*******************************/ +static int +onvm_nflib_dpdk_init(int argc, char *argv[]) { + int retval_eal = 0; + if ((retval_eal = rte_eal_init(argc, argv)) < 0) + return -1; + return retval_eal; +} + +static int +onvm_nflib_lookup_shared_structs(void) { + const struct rte_memzone *mz_nf; + const struct rte_memzone *mz_port; + const struct rte_memzone *mz_cores; + const struct rte_memzone *mz_scp; + const struct rte_memzone *mz_services; + const struct rte_memzone *mz_nf_per_service; + const struct rte_memzone *mz_onvm_config; + struct rte_mempool *mp; + struct onvm_service_chain **scp; + + /* Lookup mempool for nf_init_cfg struct */ + nf_init_cfg_mp = rte_mempool_lookup(_NF_MEMPOOL_NAME); + if (nf_init_cfg_mp == NULL) + rte_exit(EXIT_FAILURE, "No NF Info mempool - bye\n"); + + /* Lookup mempool for NF messages */ + nf_msg_pool = rte_mempool_lookup(_NF_MSG_POOL_NAME); + if (nf_msg_pool == NULL) + rte_exit(EXIT_FAILURE, "No NF Message mempool - bye\n"); + + mp = rte_mempool_lookup(PKTMBUF_POOL_NAME); + if (mp == NULL) + rte_exit(EXIT_FAILURE, "Cannot get mempool for mbufs\n"); + + /* Lookup mempool for NF structs */ + mz_nf = rte_memzone_lookup(MZ_NF_INFO); + if (mz_nf == NULL) + rte_exit(EXIT_FAILURE, "Cannot get NF structure mempool\n"); + nfs = mz_nf->addr; + + mz_services = rte_memzone_lookup(MZ_SERVICES_INFO); + if (mz_services == NULL) { + rte_exit(EXIT_FAILURE, "Cannot get service information\n"); + } + services = mz_services->addr; + + mz_nf_per_service = rte_memzone_lookup(MZ_NF_PER_SERVICE_INFO); + if (mz_nf_per_service == NULL) { + rte_exit(EXIT_FAILURE, "Cannot get NF per service information\n"); + } + nf_per_service_count = mz_nf_per_service->addr; + + mz_port = rte_memzone_lookup(MZ_PORT_INFO); + if (mz_port == NULL) + rte_exit(EXIT_FAILURE, "Cannot get port info structure\n"); + ports = mz_port->addr; + + mz_cores = rte_memzone_lookup(MZ_CORES_STATUS); + if (mz_cores == NULL) + rte_exit(EXIT_FAILURE, "Cannot get core status structure\n"); + cores = mz_cores->addr; + + mz_onvm_config = rte_memzone_lookup(MZ_ONVM_CONFIG); + if (mz_onvm_config == NULL) + rte_exit(EXIT_FAILURE, "Cannot get onvm config\n"); + onvm_config = mz_onvm_config->addr; + onvm_nflib_parse_config(onvm_config); + + mz_scp = rte_memzone_lookup(MZ_SCP_INFO); + if (mz_scp == NULL) + rte_exit(EXIT_FAILURE, "Cannot get service chain info structre\n"); + scp = mz_scp->addr; + default_chain = *scp; + onvm_sc_print(default_chain); + + mgr_msg_queue = rte_ring_lookup(_MGR_MSG_QUEUE_NAME); + if (mgr_msg_queue == NULL) + rte_exit(EXIT_FAILURE, "Cannot get mgr message ring"); + + return 0; +} + +static void +onvm_nflib_parse_config(struct onvm_configuration *config) { + ONVM_NF_SHARE_CORES = config->flags.ONVM_NF_SHARE_CORES; +} static inline uint16_t -onvm_nflib_dequeue_packets(void **pkts, struct onvm_nf *nf, pkt_handler_func handler) { - struct onvm_pkt_meta* meta; +onvm_nflib_dequeue_packets(void **pkts, struct onvm_nf_local_ctx *nf_local_ctx, nf_pkt_handler_fn handler) { + struct onvm_nf *nf; + struct onvm_pkt_meta *meta; uint16_t i, nb_pkts; struct packet_buf tx_buf; int ret_act; + nf = nf_local_ctx->nf; + /* Dequeue all packets in ring up to max possible. */ nb_pkts = rte_ring_dequeue_burst(nf->rx_q, pkts, PACKET_READ_SIZE, NULL); - /* Probably want to comment this out */ - if(unlikely(nb_pkts == 0)) { + /* Possibly sleep if in shared core mode, otherwise return */ + if (unlikely(nb_pkts == 0)) { + if (ONVM_NF_SHARE_CORES) { + rte_atomic16_set(nf->shared_core.sleep_state, 1); + sem_wait(nf->shared_core.nf_mutex); + } return 0; } @@ -748,10 +921,10 @@ onvm_nflib_dequeue_packets(void **pkts, struct onvm_nf *nf, pkt_handler_func han /* Give each packet to the user proccessing function */ for (i = 0; i < nb_pkts; i++) { - meta = onvm_get_pkt_meta((struct rte_mbuf*)pkts[i]); - ret_act = (*handler)((struct rte_mbuf*)pkts[i], meta, nf->info); + meta = onvm_get_pkt_meta((struct rte_mbuf *)pkts[i]); + ret_act = (*handler)((struct rte_mbuf *)pkts[i], meta, nf_local_ctx); /* NF returns 0 to return packets or 1 to buffer */ - if(likely(ret_act == 0)) { + if (likely(ret_act == 0)) { tx_buf.buffer[tx_buf.count++] = pkts[i]; } else { nf->stats.tx_buffer++; @@ -759,77 +932,66 @@ onvm_nflib_dequeue_packets(void **pkts, struct onvm_nf *nf, pkt_handler_func han } if (ONVM_NF_HANDLE_TX) { return nb_pkts; - } + } onvm_pkt_enqueue_tx_thread(&tx_buf, nf); return 0; } static inline void -onvm_nflib_dequeue_messages(struct onvm_nf *nf) { +onvm_nflib_dequeue_messages(struct onvm_nf_local_ctx *nf_local_ctx) { struct onvm_nf_msg *msg; struct rte_ring *msg_q; - msg_q = nf->msg_q; + msg_q = nf_local_ctx->nf->msg_q; // Check and see if this NF has any messages from the manager if (likely(rte_ring_count(msg_q) == 0)) { return; } msg = NULL; - rte_ring_dequeue(msg_q, (void**)(&msg)); - onvm_nflib_handle_msg(msg, nf->info); - rte_mempool_put(nf_msg_pool, (void*)msg); + rte_ring_dequeue(msg_q, (void **)(&msg)); + onvm_nflib_handle_msg(msg, nf_local_ctx); + rte_mempool_put(nf_msg_pool, (void *)msg); } static void * onvm_nflib_start_child(void *arg) { struct onvm_nf *parent; struct onvm_nf *child; - struct onvm_nf_info *child_info; + struct onvm_nf_init_cfg *child_nf_init_cfg; struct onvm_nf_scale_info *scale_info; + struct onvm_nf_local_ctx *child_context; - scale_info = (struct onvm_nf_scale_info *) arg; + scale_info = (struct onvm_nf_scale_info *)arg; + child_context = onvm_nflib_init_nf_local_ctx(); parent = &nfs[scale_info->parent->instance_id]; /* Initialize the info struct */ - child_info = onvm_nflib_info_init(parent->info->tag); - - /* Set child NF service and instance id */ - child_info->service_id = scale_info->service_id; - child_info->instance_id = scale_info->instance_id; - - /* Set child NF core options */ - child_info->core = scale_info->flags; - child_info->flags = scale_info->flags; - - RTE_LOG(INFO, APP, "Starting child NF with service %u, instance id %u\n", child_info->service_id, child_info->instance_id); - onvm_nflib_start_nf(child_info); - - child = &nfs[child_info->instance_id]; - /* Save the parent id for future clean up */ - child->parent = parent->instance_id; - /* Save nf specifc functions for possible future use */ - child->nf_setup_function = scale_info->setup_func; - child->nf_pkt_function = scale_info->pkt_func; - child->nf_callback_function = scale_info->callback_func; - child->nf_advanced_rings_function = scale_info->adv_rings_func; - /* Set nf state data */ - child_info->data = scale_info->data; - - if (child->nf_pkt_function){ - onvm_nflib_run_callback(child_info, child->nf_pkt_function, child->nf_callback_function); - } else if (child->nf_advanced_rings_function) { - if (scale_info->setup_func != NULL) - scale_info->setup_func(child_info); - onvm_nflib_nf_ready(child_info); - scale_info->adv_rings_func(child_info); - } else { - /* Sanity check */ - rte_exit(EXIT_FAILURE, "Spawned NF doesn't have a pkt_handler or an advanced rings function\n"); + child_nf_init_cfg = scale_info->nf_init_cfg; + + RTE_LOG(INFO, APP, "Starting child NF with service %u, instance id %u\n", child_nf_init_cfg->service_id, + child_nf_init_cfg->instance_id); + if (onvm_nflib_start_nf(child_context, child_nf_init_cfg) < 0) { + onvm_nflib_stop(child_context); + return NULL; } + child = &nfs[child_nf_init_cfg->instance_id]; + child_context->nf = child; + /* Save the parent id for future clean up */ + child->thread_info.parent = parent->instance_id; + /* Save the nf specifc function table */ + child->function_table = scale_info->function_table; + /* Set nf state data */ + child->data = scale_info->data; + + onvm_nflib_run(child_context); + + /* Clean up after the child */ + onvm_nflib_stop(child_context); + if (scale_info != NULL) { rte_free(scale_info); scale_info = NULL; @@ -838,96 +1000,114 @@ onvm_nflib_start_child(void *arg) { return NULL; } -static int -onvm_nflib_is_scale_info_valid(struct onvm_nf_scale_info *scale_info) { - if (scale_info->service_id == 0 || - (scale_info->pkt_func == NULL && scale_info->adv_rings_func == NULL) || - (scale_info->pkt_func != NULL && scale_info->adv_rings_func != NULL)) - return -1; +void +onvm_nflib_handle_signal(int sig) { + struct onvm_nf *nf; - return 0; -} + if (sig != SIGINT && sig != SIGTERM) + return; -static struct onvm_nf_info * -onvm_nflib_info_init(const char *tag) -{ - void *mempool_data; - struct onvm_nf_info *info; + /* Stops both starting and running NFs */ + rte_atomic16_set(&main_nf_local_ctx->keep_running, 0); - if (rte_mempool_get(nf_info_mp, &mempool_data) < 0) { - rte_exit(EXIT_FAILURE, "Failed to get nf info memory\n"); + /* If NF didn't start yet no cleanup is necessary */ + if (rte_atomic16_read(&main_nf_local_ctx->nf_init_finished) == 0) { + return; } - if (mempool_data == NULL) { - rte_exit(EXIT_FAILURE, "Client Info struct not allocated\n"); + /* If NF is asleep, wake it up */ + nf = main_nf_local_ctx->nf; + if (ONVM_NF_SHARE_CORES && rte_atomic16_read(nf->shared_core.sleep_state) == 1) { + rte_atomic16_set(nf->shared_core.sleep_state, 0); + sem_post(nf->shared_core.nf_mutex); } - info = (struct onvm_nf_info*) mempool_data; - info->instance_id = NF_NO_ID; - info->core = rte_lcore_id(); - info->flags = 0; - info->status = NF_WAITING_FOR_ID; - info->tag = tag; + if (global_nf_signal_handler != NULL) + global_nf_signal_handler(sig); - return info; + /* All the child termination will be done later in onvm_nflib_stop */ } -static void -onvm_nflib_nf_tx_mgr_init(struct onvm_nf *nf) -{ +static int +onvm_nflib_is_scale_info_valid(struct onvm_nf_scale_info *scale_info) { + return scale_info->nf_init_cfg->service_id != 0 && scale_info->function_table != NULL && + scale_info->function_table->pkt_handler != NULL; +} + + +static void +onvm_nflib_nf_tx_mgr_init(struct onvm_nf *nf) { nf->nf_tx_mgr = calloc(1, sizeof(struct queue_mgr)); - nf->nf_tx_mgr->mgr_type_t = NF; + nf->nf_tx_mgr->mgr_type_t = NF; nf->nf_tx_mgr->to_tx_buf = calloc(1, sizeof(struct packet_buf)); - nf->nf_tx_mgr->id = nf->info->instance_id; + nf->nf_tx_mgr->id = nf->instance_id; nf->nf_tx_mgr->nf_rx_bufs = calloc(MAX_NFS, sizeof(struct packet_buf)); } - static void onvm_nflib_usage(const char *progname) { - printf("Usage: %s [EAL args] -- " - "[-n ]" - "[-r ]" - "[-m (manual core assignment flag)]" - "[-s (share core flag)]\n\n", progname); + printf( + "Usage: %s [EAL args] -- " + "[-n ] " + "[-r ] " + "[-t ] " + "[-l ] " + "[-m (manual core assignment flag)] " + "[-s (share core flag)]\n\n", + progname); } - static int -onvm_nflib_parse_args(int argc, char *argv[], struct onvm_nf_info *nf_info) { +onvm_nflib_parse_args(int argc, char *argv[], struct onvm_nf_init_cfg *nf_init_cfg) { const char *progname = argv[0]; int c, initial_instance_id; int service_id = -1; opterr = 0; - while ((c = getopt (argc, argv, "n:r:ms")) != -1) + while ((c = getopt (argc, argv, "n:r:t:l:ms")) != -1) switch (c) { - case 'n': - initial_instance_id = (uint16_t) strtoul(optarg, NULL, 10); - nf_info->instance_id = initial_instance_id; - break; - case 'r': - service_id = (uint16_t) strtoul(optarg, NULL, 10); - // Service id 0 is reserved - if (service_id == 0) service_id = -1; - break; - case 'm': - nf_info->flags = ONVM_SET_BIT(nf_info->flags, MANUAL_CORE_ASSIGNMENT_BIT); - break; - case 's': - nf_info->flags = ONVM_SET_BIT(nf_info->flags, SHARE_CORE_BIT); - break; - case '?': - onvm_nflib_usage(progname); - if (optopt == 'n') - fprintf(stderr, "Option -%c requires an argument.\n", optopt); - else if (isprint(optopt)) - fprintf(stderr, "Unknown option `-%c'.\n", optopt); - else - fprintf(stderr, "Unknown option character `\\x%x'.\n", optopt); - return -1; - default: - return -1; + case 'n': + initial_instance_id = (uint16_t)strtoul(optarg, NULL, 10); + nf_init_cfg->instance_id = initial_instance_id; + break; + case 'r': + service_id = (uint16_t)strtoul(optarg, NULL, 10); + // Service id 0 is reserved + if (service_id == 0) + service_id = -1; + break; + case 't': + nf_init_cfg->time_to_live = (uint16_t) strtoul(optarg, NULL, 10); + if (nf_init_cfg->time_to_live == 0) { + fprintf(stderr, "Time to live argument can't be 0\n"); + return -1; + } + break; + case 'l': + nf_init_cfg->pkt_limit = (uint16_t) strtoul(optarg, NULL, 10); + if (nf_init_cfg->pkt_limit == 0) { + fprintf(stderr, "Packet time to live argument can't be 0\n"); + return -1; + } + break; + case 'm': + nf_init_cfg->init_options = ONVM_SET_BIT(nf_init_cfg->init_options, + MANUAL_CORE_ASSIGNMENT_BIT); + break; + case 's': + nf_init_cfg->init_options = ONVM_SET_BIT(nf_init_cfg->init_options, SHARE_CORE_BIT); + break; + case '?': + onvm_nflib_usage(progname); + if (optopt == 'n') + fprintf(stderr, "Option -%c requires an argument.\n", optopt); + else if (isprint(optopt)) + fprintf(stderr, "Unknown option `-%c'.\n", optopt); + else + fprintf(stderr, "Unknown option character `\\x%x'.\n", optopt); + return -1; + default: + return -1; } if (service_id == (uint16_t)-1) { @@ -935,50 +1115,131 @@ onvm_nflib_parse_args(int argc, char *argv[], struct onvm_nf_info *nf_info) { fprintf(stderr, "You must provide a nonzero service ID with -r\n"); return -1; } - nf_info->service_id = service_id; + nf_init_cfg->service_id = service_id; return optind; } - static void -onvm_nflib_handle_signal(int sig) -{ - if (sig == SIGINT || sig == SIGTERM) - keep_running = 0; +onvm_nflib_terminate_children(struct onvm_nf *nf) { + uint16_t i, iter_cnt; + + iter_cnt = 0; + /* Keep trying to shutdown children until there are none left */ + while (rte_atomic16_read(&nf->thread_info.children_cnt) > 0 && iter_cnt < NF_TERM_STOP_ITER_TIMES) { + for (i = 0; i < MAX_NFS; i++) { + if (nfs[i].thread_info.parent != nf->instance_id) + continue; + + if (!onvm_nf_is_valid(&nfs[i])) + continue; + + /* Wake up the child if its sleeping */ + if (ONVM_NF_SHARE_CORES && rte_atomic16_read(nfs[i].shared_core.sleep_state) == 1) { + rte_atomic16_set(nfs[i].shared_core.sleep_state, 0); + sem_post(nfs[i].shared_core.nf_mutex); + } + } + RTE_LOG(INFO, APP, "NF %d: Waiting for %d children to exit\n", + nf->instance_id, rte_atomic16_read(&nf->thread_info.children_cnt)); + sleep(NF_TERM_WAIT_TIME); + iter_cnt++; + } + + if (rte_atomic16_read(&nf->thread_info.children_cnt) > 0) { + RTE_LOG(INFO, APP, "NF %d: Up to %d children may still be running and must be killed manually\n", + nf->instance_id, rte_atomic16_read(&nf->thread_info.children_cnt)); + } } static void -onvm_nflib_cleanup(struct onvm_nf_info *nf_info) -{ - if (nf_info == NULL) { +onvm_nflib_cleanup(struct onvm_nf_local_ctx *nf_local_ctx) { + struct onvm_nf_msg *shutdown_msg; + struct onvm_nf *nf; + + if (nf_local_ctx == NULL) { return; } - if (nf_info->data != NULL) { - rte_free(nf_info->data); - nf_info->data = NULL; + /* In case init wasn't finished */ + if (rte_atomic16_read(&nf_local_ctx->nf_init_finished) == 0) { + free(nf_local_ctx); + nf_local_ctx = NULL; + return; } - struct onvm_nf_msg *shutdown_msg; + nf = nf_local_ctx->nf; - /* Put this NF's info struct back into queue for manager to ack shutdown */ - if (mgr_msg_queue == NULL) { - rte_mempool_put(nf_info_mp, nf_info); // give back mermory - rte_exit(EXIT_FAILURE, "Cannot get nf_info ring for shutdown"); + /* Sanity check */ + if (nf == NULL) { + rte_exit(EXIT_FAILURE, "NF init finished but context->nf is NULL"); } - if (rte_mempool_get(nf_msg_pool, (void**)(&shutdown_msg)) != 0) { - rte_mempool_put(nf_info_mp, nf_info); // give back mermory - rte_exit(EXIT_FAILURE, "Cannot create shutdown msg"); + + /* Cleanup state data */ + if (nf->data != NULL) { + rte_free(nf->data); + nf->data = NULL; + } + + /* Cleanup for the nf_tx_mgr pointers */ + if (nf->nf_tx_mgr) { + if (nf->nf_tx_mgr->to_tx_buf != NULL) { + free(nf->nf_tx_mgr->to_tx_buf); + nf->nf_tx_mgr->to_tx_buf = NULL; + } + if (nf->nf_tx_mgr->nf_rx_bufs != NULL) { + free(nf->nf_tx_mgr->nf_rx_bufs); + nf->nf_tx_mgr->nf_rx_bufs = NULL; + } + free(nf->nf_tx_mgr); + nf->nf_tx_mgr = NULL; + } + + if (nf->function_table) { + free(nf->function_table); + nf->function_table = NULL; } + if (mgr_msg_queue == NULL) + rte_exit(EXIT_FAILURE, "Cannot get mgr message ring for shutdown"); + if (rte_mempool_get(nf_msg_pool, (void **)(&shutdown_msg)) != 0) + rte_exit(EXIT_FAILURE, "Cannot create shutdown msg"); + shutdown_msg->msg_type = MSG_NF_STOPPING; - shutdown_msg->msg_data = nf_info; + shutdown_msg->msg_data = nf; if (rte_ring_enqueue(mgr_msg_queue, shutdown_msg) < 0) { - rte_mempool_put(nf_info_mp, nf_info); // give back mermory rte_mempool_put(nf_msg_pool, shutdown_msg); - rte_exit(EXIT_FAILURE, "Cannot send nf_info to manager for shutdown"); + rte_exit(EXIT_FAILURE, "Cannot send mgr message to manager for shutdown"); } + /* Cleanup context */ + nf_local_ctx->nf = NULL; + free(nf_local_ctx); +} + +static void +init_shared_core_mode_info(uint16_t instance_id) { + key_t key; + int shmid; + char *shm; + struct onvm_nf *nf; + const char *sem_name; + + nf = &nfs[instance_id]; + sem_name = get_sem_name(instance_id); + + nf->shared_core.nf_mutex = sem_open(sem_name, 0, 0666, 0); + if (nf->shared_core.nf_mutex == SEM_FAILED) + rte_exit(EXIT_FAILURE, "Unable to execute semphore for NF %d\n", instance_id); + + /* Get flag which is shared by server */ + key = get_rx_shmkey(instance_id); + if ((shmid = shmget(key, SHMSZ, 0666)) < 0) + rte_exit(EXIT_FAILURE, "Unable to locate the segment for NF %d\n", instance_id); + + if ((shm = shmat(shmid, NULL, 0)) == (char *) -1) + rte_exit(EXIT_FAILURE, "Can not attach the shared segment to the NF space for NF %d\n", instance_id); + + nf->shared_core.sleep_state = (rte_atomic16_t *)shm; } diff --git a/onvm/onvm_nflib/onvm_nflib.h b/onvm/onvm_nflib/onvm_nflib.h index 1065c2d97..9cacf4983 100644 --- a/onvm/onvm_nflib/onvm_nflib.h +++ b/onvm/onvm_nflib/onvm_nflib.h @@ -5,9 +5,9 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside - * 2016-2017 Hewlett Packard Enterprise Development LP + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside + * 2016-2019 Hewlett Packard Enterprise Development LP * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,7 +38,6 @@ * ********************************************************************/ - /****************************************************************************** onvm_nflib.h @@ -49,7 +48,6 @@ ******************************************************************************/ - #ifndef _ONVM_NFLIB_H_ #define _ONVM_NFLIB_H_ #include @@ -59,6 +57,38 @@ /************************************API**************************************/ +/** + * Initialize the starting OpenNetVM NF context. + * + * @return + * Pointer to the created NF context + */ +struct onvm_nf_local_ctx * +onvm_nflib_init_nf_local_ctx(void); + +/** + * Initialize an empty NF functional table + * + * @return + * Pointer to the created function table struct + */ +struct onvm_nf_function_table * +onvm_nflib_init_nf_function_table(void); + +/** + * Initialize the default OpenNetVM signal handling. + * + * @param nf_local_ctx + * Pointer to a context struct of this NF. + * @param signal_handler + * Function pointer to an optional NF specific signal handler function, + * that will be called after the default onvm signal handler. + * @return + * Error code or 0 if succesfull + */ +int +onvm_nflib_start_signal_handler(struct onvm_nf_local_ctx *nf_local_ctx, handle_signal_func signal_hanlder); + /** * Initialize the OpenNetVM container Library. * This will setup the DPDK EAL as a secondary process, and notify the host @@ -71,65 +101,44 @@ * @param tag * A uniquely identifiable string for this NF. * For example, can be the application name (e.g. "bridge_nf") - * @param info - * A double pointer to the structure containing information relevant to this NF. - * For example, the instance_id and the status of the NF can be found here. + * @param nf_local_ctx + * Pointer to a context struct of this NF. + * @param nf_function_table + * Pointer to a function table struct for this NF * @return * On success, the number of parsed arguments, which is greater or equal to * zero. After the call to onvm_nf_init(), all arguments argv[x] with x < ret * may be modified and should not be accessed by the application., - * On error, a negative value . + * On error, a negative value. */ int -onvm_nflib_init(int argc, char *argv[], const char *nf_tag, struct onvm_nf_info **nf_info_p); - +onvm_nflib_init(int argc, char *argv[], const char *nf_tag, struct onvm_nf_local_ctx *nf_local_ctx, + struct onvm_nf_function_table *nf_function_table); /** - * Run the OpenNetVM container Library. - * This will register the callback used for each new packet, and the callback used for batch processing. It will then - * loop forever waiting for packets. + * Runs the OpenNetVM container library. * - * @param info - * A pointer to the info struct describing this NF app. Must be from a huge page memzone. - * @param handler - * A pointer to the function that will be called on each received packet. - * @param callback_handler - * A pointer to the callback handler that is called every attempted batch + * @param nf_local_ctx + * Pointer to a context struct of this NF. * @return * 0 on success, or a negative value on error. */ int -onvm_nflib_run_callback(struct onvm_nf_info* info, pkt_handler_func pkt_handler, callback_handler_func callback_handler); - - -/** - * Runs the OpenNetVM container library, without using the callback function. - * It calls the onvm_nflib_run_callback function with only the passed packet handler, and uses null for callback - * - * @param info - * An info struct describing this NF. Must be from a huge page memzone. - * @param handler - * A pointer to the function that will be called on each received packet. - * @return - * 0 on success, or a negative value on error. - */ -int -onvm_nflib_run(struct onvm_nf_info* info, pkt_handler_func pkt_handler); +onvm_nflib_run(struct onvm_nf_local_ctx *nf_local_ctx); /** * Return a packet that was created by the NF or has previously had the * ONVM_NF_ACTION_BUFFER action called on it. * - * @param info - * Pointer to a struct containing information used to describe this NF. + * @param nf + * Pointer to a struct containing information about this NF. * @param pkt * a pointer to a packet that should now have a action other than buffer. * @return * 0 on success, or a negative value on error. */ int -onvm_nflib_return_pkt(struct onvm_nf_info *nf_info, struct rte_mbuf* pkt); - +onvm_nflib_return_pkt(struct onvm_nf *nf, struct rte_mbuf *pkt); /** * Return a group of packets that were created by the NF or have previously had the @@ -143,136 +152,131 @@ onvm_nflib_return_pkt(struct onvm_nf_info *nf_info, struct rte_mbuf* pkt); * 0 on success, or a negative value on error (-1 if bad arguments, -ENOBUFS if enqueue fails). */ int -onvm_nflib_return_pkt_bulk(struct onvm_nf_info *nf_info, struct rte_mbuf** pkts, uint16_t count); - +onvm_nflib_return_pkt_bulk(struct onvm_nf *nf, struct rte_mbuf **pkts, uint16_t count); /** * Inform the manager that the NF is ready to receive packets. * This only needs to be called when the NF is using advanced rings * Otherwise, onvm_nflib_run will call this * - * @param info - * A pointer to this NF's info struct + * @param nf + * Pointer to a struct containing information about this NF. * @return * 0 on success, or a negative value on failure */ int -onvm_nflib_nf_ready(struct onvm_nf_info *info); +onvm_nflib_nf_ready(struct onvm_nf *nf); + +/* + * Start the NF by signaling manager that its ready to recieve packets + * + * Input: Pointer to context struct of this NF + */ +int +onvm_nflib_start_nf(struct onvm_nf_local_ctx *nf_local_ctx, struct onvm_nf_init_cfg *nf_init_cfg); /** * Process an message. Does stuff. * * @param msg * a pointer to a message to be processed + * @param nf_local_ctx + * Pointer to a context struct of this NF. * @return * 0 on success, or a negative value on error */ int -onvm_nflib_handle_msg(struct onvm_nf_msg *msg, __attribute__((unused)) struct onvm_nf_info *nf_info); +onvm_nflib_handle_msg(struct onvm_nf_msg *msg, struct onvm_nf_local_ctx *nf_local_ctx); + +int +onvm_nflib_send_msg_to_nf(uint16_t dest_nf, void *msg_data); /** * Stop this NF and clean up its memory * Sends shutdown message to manager. * - * @param info - * Pointer to the info struct for this NF. + * @param nf_local_ctx + * Pointer to a context struct of this NF. */ void -onvm_nflib_stop(struct onvm_nf_info *nf_info); +onvm_nflib_stop(struct onvm_nf_local_ctx *nf_local_ctx); /** - * Return the tx_ring associated with this NF. + * Function that initialize the NF init config data structure. * - * @param info - * An info struct describing this NF. - * @return - * Pointer to tx_ring structure associated with info, NULL on error. - */ -struct rte_ring * -onvm_nflib_get_tx_ring(struct onvm_nf_info* info); - - -/** - * Return the rx_ring associated with this NF. - * - * @param info - * An info struct describing this NF app. - * @return - * Pointer to rx_ring structure associated with info, NULL on error. - */ -struct rte_ring * -onvm_nflib_get_rx_ring(struct onvm_nf_info* info); - - -/** - * Return the nf details associated with this NF. + * Input : the tag to name the NF + * Output : the data structure initialized * - * @param id - * An instance id of the corresponding NF. - * @return - * Pointer to NF structure referenced by instance id, NULL on error. */ -struct onvm_nf * -onvm_nflib_get_nf(uint16_t id); +struct onvm_nf_init_cfg * +onvm_nflib_init_nf_init_cfg(const char *tag); -/** - * Set the setup function for the NF. - * Function automatically executes when calling onvm_nflib_run or when scaling. - * This will be run for "normal" mode NFs (i.e., not using advanced rings, see 'NOTE') on startup. - * - * To make a child inherit this setting, use `onvm_nflib_inherit_parent_config` to get a - * scaling struct with the parent's function pointers. +/* + * Function that initialize the NF init config data structure. + * the arguments are copied from the parent information * - * NOTE: This function doesn't work for advanced rings main NFs, but works for their children. - * For the main NF just manually call the function. + * Input : pointer to the parent NF + * Output : the data structure initialized * - * @param info - * An info struct describing this NF app. - * @param setup - * A NF setup function that runs before running the NF. */ -void -onvm_nflib_set_setup_function(struct onvm_nf_info* info, setup_func setup); +struct onvm_nf_init_cfg * +onvm_nflib_inherit_parent_init_cfg(struct onvm_nf *parent); /** * Allocates an empty scaling config to be filled in by the NF. * Defines the instance_id to NF_NO_ID.. * - * @param info - * An info struct describing this NF app. + * @param nf + * An onvm_nf struct describing this NF. * @return * Pointer to onvm_nf_scale_info structure for running onvm_nflib_scale */ struct onvm_nf_scale_info * -onvm_nflib_get_empty_scaling_config(struct onvm_nf_info *parent_info); - +onvm_nflib_get_empty_scaling_config(struct onvm_nf *nf); /** * Fill the onvm_nflib_scale_info with the infromation of the parent, inherits * service id, pkt functions(setup, pkt_handler, callback, advanced rings). * - * @param info - * An info struct describing this NF app. - * Data pointer for the scale_info. + * @param nf + * An onvm_nf struct describing this NF. + * @param data + * Void data pointer for the scale_info. * @return * Pointer to onvm_nf_scale_info structure which can be used to run onvm_nflib_scale */ struct onvm_nf_scale_info * -onvm_nflib_inherit_parent_config(struct onvm_nf_info *parent_info, void *data); +onvm_nflib_inherit_parent_config(struct onvm_nf *nf, void *data); /* * Scales the NF. Determines the core to scale to, and starts a new thread for the NF. * - * @param id - * An Info struct describing this NF app. + * @param scale_info + * A scale info struct describing the child NF to be launched * @return * Error code or 0 if successful. */ int onvm_nflib_scale(struct onvm_nf_scale_info *scale_info); +/** + * Request LPM memory region. Returns the success or failure of this initialization. + * + * @param lpm_request + * An LPM request struct to initialize the LPM region + * @return + * Request response status + */ +int +onvm_nflib_request_lpm(struct lpm_request *req); struct onvm_service_chain * onvm_nflib_get_default_chain(void); -#endif // _ONVM_NFLIB_H_ +/** + * Retrieves custom onvm flags + */ +struct onvm_configuration * +onvm_nflib_get_onvm_config(void); + +#endif // _ONVM_NFLIB_H_ diff --git a/onvm/onvm_nflib/onvm_pkt_common.c b/onvm/onvm_nflib/onvm_pkt_common.c index d019f2476..9dd738490 100644 --- a/onvm/onvm_nflib/onvm_pkt_common.c +++ b/onvm/onvm_nflib/onvm_pkt_common.c @@ -5,9 +5,9 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside - * 2010-2014 Intel Corporation. All rights reserved. + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside + * 2010-2019 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,7 +38,6 @@ * ********************************************************************/ - /****************************************************************************** onvm_pkt_common.c @@ -47,13 +46,10 @@ ******************************************************************************/ - #include "onvm_pkt_common.h" - /**********************Internal Functions Prototypes**************************/ - /* * Function to enqueue a packet on one port's queue. * @@ -76,7 +72,6 @@ onvm_pkt_enqueue_port(struct queue_mgr *tx_mgr, uint16_t port, struct rte_mbuf * static inline void onvm_pkt_process_next_action(struct queue_mgr *tx_mgr, struct rte_mbuf *pkt, struct onvm_nf *nf); - /* * Helper function to drop a packet. * @@ -88,10 +83,8 @@ onvm_pkt_process_next_action(struct queue_mgr *tx_mgr, struct rte_mbuf *pkt, str static int onvm_pkt_drop(struct rte_mbuf *pkt); - /**********************************Interfaces*********************************/ - void onvm_pkt_process_tx_batch(struct queue_mgr *tx_mgr, struct rte_mbuf *pkts[], uint16_t tx_count, struct onvm_nf *nf) { uint16_t i; @@ -102,7 +95,7 @@ onvm_pkt_process_tx_batch(struct queue_mgr *tx_mgr, struct rte_mbuf *pkts[], uin return; for (i = 0; i < tx_count; i++) { - meta = (struct onvm_pkt_meta*) &(((struct rte_mbuf*)pkts[i])->udata64); + meta = (struct onvm_pkt_meta *)&(((struct rte_mbuf *)pkts[i])->udata64); meta->src = nf->instance_id; if (meta->action == ONVM_NF_ACTION_DROP) { // if the packet is drop, then is 0 @@ -166,13 +159,12 @@ onvm_pkt_flush_nf_queue(struct queue_mgr *tx_mgr, uint16_t nf_id, struct onvm_nf if (!onvm_nf_is_valid(nf)) return; - if (rte_ring_enqueue_bulk(nf->rx_q, (void **)nf_buf->buffer, - nf_buf->count, NULL) == 0) { + if (rte_ring_enqueue_bulk(nf->rx_q, (void **)nf_buf->buffer, nf_buf->count, NULL) == 0) { for (i = 0; i < nf_buf->count; i++) { onvm_pkt_drop(nf_buf->buffer[i]); } nf->stats.rx_drop += nf_buf->count; - if (source_nf != NULL) + if (source_nf != NULL) source_nf->stats.tx_drop += nf_buf->count; } else { nf->stats.rx += nf_buf->count; @@ -183,12 +175,12 @@ onvm_pkt_flush_nf_queue(struct queue_mgr *tx_mgr, uint16_t nf_id, struct onvm_nf } void -onvm_pkt_enqueue_nf(struct queue_mgr *tx_mgr, uint16_t dst_service_id, struct rte_mbuf *pkt, struct onvm_nf *source_nf) { +onvm_pkt_enqueue_nf(struct queue_mgr *tx_mgr, uint16_t dst_service_id, struct rte_mbuf *pkt, + struct onvm_nf *source_nf) { struct onvm_nf *nf; uint16_t dst_instance_id; struct packet_buf *nf_buf; - if (tx_mgr == NULL || pkt == NULL) return; @@ -231,10 +223,7 @@ onvm_pkt_flush_port_queue(struct queue_mgr *tx_mgr, uint16_t port) { return; tx_stats = &(ports->tx_stats); - sent = rte_eth_tx_burst(port, - tx_mgr->id, - port_buf->buffer, - port_buf->count); + sent = rte_eth_tx_burst(port, tx_mgr->id, port_buf->buffer, port_buf->count); if (unlikely(sent < port_buf->count)) { for (i = sent; i < port_buf->count; i++) { onvm_pkt_drop(port_buf->buffer[i]); @@ -246,14 +235,15 @@ onvm_pkt_flush_port_queue(struct queue_mgr *tx_mgr, uint16_t port) { port_buf->count = 0; } -void +void onvm_pkt_enqueue_tx_thread(struct packet_buf *pkt_buf, struct onvm_nf *nf) { uint16_t i; if (pkt_buf->count == 0) return; - if (unlikely(pkt_buf->count > 0 && rte_ring_enqueue_bulk(nf->tx_q, (void **)pkt_buf->buffer, pkt_buf->count, NULL) == 0)) { + if (unlikely(pkt_buf->count > 0 && + rte_ring_enqueue_bulk(nf->tx_q, (void **)pkt_buf->buffer, pkt_buf->count, NULL) == 0)) { nf->stats.tx_drop += pkt_buf->count; for (i = 0; i < pkt_buf->count; i++) { rte_pktmbuf_free(pkt_buf->buffer[i]); @@ -264,10 +254,8 @@ onvm_pkt_enqueue_tx_thread(struct packet_buf *pkt_buf, struct onvm_nf *nf) { pkt_buf->count = 0; } - /****************************Internal functions*******************************/ - inline static void onvm_pkt_enqueue_port(struct queue_mgr *tx_mgr, uint16_t port, struct rte_mbuf *buf) { struct packet_buf *port_buf; @@ -282,10 +270,8 @@ onvm_pkt_enqueue_port(struct queue_mgr *tx_mgr, uint16_t port, struct rte_mbuf * } } - inline static void onvm_pkt_process_next_action(struct queue_mgr *tx_mgr, struct rte_mbuf *pkt, struct onvm_nf *nf) { - if (tx_mgr == NULL || pkt == NULL || nf == NULL) return; @@ -324,10 +310,8 @@ onvm_pkt_process_next_action(struct queue_mgr *tx_mgr, struct rte_mbuf *pkt, str (meta->chain_index)++; } - /*******************************Helper function*******************************/ - static int onvm_pkt_drop(struct rte_mbuf *pkt) { rte_pktmbuf_free(pkt); diff --git a/onvm/onvm_nflib/onvm_pkt_common.h b/onvm/onvm_nflib/onvm_pkt_common.h index 76ff3d1eb..3a320c796 100644 --- a/onvm/onvm_nflib/onvm_pkt_common.h +++ b/onvm/onvm_nflib/onvm_pkt_common.h @@ -5,9 +5,9 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside - * 2010-2014 Intel Corporation. All rights reserved. + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside + * 2010-2019 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,7 +38,6 @@ * ********************************************************************/ - /****************************************************************************** onvm_pkt_common.h @@ -49,14 +48,13 @@ ******************************************************************************/ - #ifndef _ONVM_PKT_COMMON_H_ #define _ONVM_PKT_COMMON_H_ #include "onvm_common.h" -#include "onvm_sc_common.h" -#include "onvm_includes.h" #include "onvm_flow_dir.h" +#include "onvm_includes.h" +#include "onvm_sc_common.h" #include "onvm_sc_mgr.h" extern struct port_info *ports; @@ -64,11 +62,10 @@ extern struct onvm_service_chain *default_chain; /*********************************Interfaces**********************************/ - /* * Interface to process packets in a given TX queue. * - * Inputs : a pointer to the tx queue + * Inputs : a pointer to the tx queue * an array of packets * the size of the array * a pointer to the NF possessing the TX queue. @@ -80,7 +77,7 @@ onvm_pkt_process_tx_batch(struct queue_mgr *tx_mgr, struct rte_mbuf *pkts[], uin /* * Interface to send packets to all NFs after processing them. * - * Input : a pointer to the tx queue + * Input : a pointer to the tx queue * a pointer to the NF possessing the TX queue. * */ @@ -126,8 +123,7 @@ onvm_pkt_flush_port_queue(struct queue_mgr *tx_mgr, uint16_t port); * a pointer to the NF * */ -void +void onvm_pkt_enqueue_tx_thread(struct packet_buf *pkt_buf, struct onvm_nf *nf); - #endif // _ONVM_PKT_COMMON_H_ diff --git a/onvm/onvm_nflib/onvm_pkt_helper.c b/onvm/onvm_nflib/onvm_pkt_helper.c index 05bab0924..1fd163c51 100644 --- a/onvm/onvm_nflib/onvm_pkt_helper.c +++ b/onvm/onvm_nflib/onvm_pkt_helper.c @@ -5,8 +5,8 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -40,26 +40,27 @@ #include "onvm_pkt_helper.h" #include "onvm_common.h" +#include "onvm_pkt_common.h" #include #include #include -#include #include +#include #include #include #include -#include #include +#include int -onvm_pkt_set_mac_addr(struct rte_mbuf* pkt, unsigned src_port_id, unsigned dst_port_id, struct port_info *ports) { - struct ether_hdr *eth; +onvm_pkt_set_mac_addr(struct rte_mbuf* pkt, unsigned src_port_id, unsigned dst_port_id, struct port_info* ports) { + struct ether_hdr* eth; - if (unlikely(pkt == NULL)) { // We do not expect to swap macs for empty packets + if (unlikely(pkt == NULL)) { // We do not expect to swap macs for empty packets return -1; } @@ -79,10 +80,10 @@ onvm_pkt_set_mac_addr(struct rte_mbuf* pkt, unsigned src_port_id, unsigned dst_p } int -onvm_pkt_swap_src_mac_addr(struct rte_mbuf* pkt, unsigned dst_port_id, struct port_info *ports) { - struct ether_hdr *eth; +onvm_pkt_swap_src_mac_addr(struct rte_mbuf* pkt, unsigned dst_port_id, struct port_info* ports) { + struct ether_hdr* eth; - if (unlikely(pkt == NULL)) { // We do not expect to swap macs for empty packets + if (unlikely(pkt == NULL)) { // We do not expect to swap macs for empty packets return -1; } @@ -106,10 +107,10 @@ onvm_pkt_swap_src_mac_addr(struct rte_mbuf* pkt, unsigned dst_port_id, struct po } int -onvm_pkt_swap_dst_mac_addr(struct rte_mbuf* pkt, unsigned src_port_id, struct port_info *ports) { - struct ether_hdr *eth; +onvm_pkt_swap_dst_mac_addr(struct rte_mbuf* pkt, unsigned src_port_id, struct port_info* ports) { + struct ether_hdr* eth; - if (unlikely(pkt == NULL)) { // We do not expect to swap macs for empty packets + if (unlikely(pkt == NULL)) { // We do not expect to swap macs for empty packets return -1; } @@ -137,14 +138,16 @@ onvm_pkt_ether_hdr(struct rte_mbuf* pkt) { if (unlikely(pkt == NULL)) { return NULL; } - return rte_pktmbuf_mtod(pkt, struct ether_hdr *); + return rte_pktmbuf_mtod(pkt, struct ether_hdr*); } struct tcp_hdr* onvm_pkt_tcp_hdr(struct rte_mbuf* pkt) { struct ipv4_hdr* ipv4 = onvm_pkt_ipv4_hdr(pkt); - if (unlikely(ipv4 == NULL)) { // Since we aren't dealing with IPv6 packets for now, we can ignore anything that isn't IPv4 + if (unlikely( + ipv4 == + NULL)) { // Since we aren't dealing with IPv6 packets for now, we can ignore anything that isn't IPv4 return NULL; } @@ -160,7 +163,9 @@ struct udp_hdr* onvm_pkt_udp_hdr(struct rte_mbuf* pkt) { struct ipv4_hdr* ipv4 = onvm_pkt_ipv4_hdr(pkt); - if (unlikely(ipv4 == NULL)) { // Since we aren't dealing with IPv6 packets for now, we can ignore anything that isn't IPv4 + if (unlikely( + ipv4 == + NULL)) { // Since we aren't dealing with IPv6 packets for now, we can ignore anything that isn't IPv4 return NULL; } @@ -187,7 +192,6 @@ onvm_pkt_ipv4_hdr(struct rte_mbuf* pkt) { return ipv4; } - int onvm_pkt_is_tcp(struct rte_mbuf* pkt) { return onvm_pkt_tcp_hdr(pkt) != NULL; @@ -203,7 +207,6 @@ onvm_pkt_is_ipv4(struct rte_mbuf* pkt) { return onvm_pkt_ipv4_hdr(pkt) != NULL; } - void onvm_pkt_print(struct rte_mbuf* pkt) { struct ipv4_hdr* ipv4 = onvm_pkt_ipv4_hdr(pkt); @@ -237,15 +240,24 @@ onvm_pkt_print_tcp(struct tcp_hdr* hdr) { printf("Flags: %" PRIx16 "\n", flags); printf("\t("); - if ((flags >> 8) & 0x1) printf("NS,"); - if ((flags >> 7) & 0x1) printf("CWR,"); - if ((flags >> 6) & 0x1) printf("ECE,"); - if ((flags >> 5) & 0x1) printf("URG,"); - if ((flags >> 4) & 0x1) printf("ACK,"); - if ((flags >> 3) & 0x1) printf("PSH,"); - if ((flags >> 2) & 0x1) printf("RST,"); - if ((flags >> 1) & 0x1) printf("SYN,"); - if (flags & 0x1) printf("FIN,"); + if ((flags >> 8) & 0x1) + printf("NS,"); + if ((flags >> 7) & 0x1) + printf("CWR,"); + if ((flags >> 6) & 0x1) + printf("ECE,"); + if ((flags >> 5) & 0x1) + printf("URG,"); + if ((flags >> 4) & 0x1) + printf("ACK,"); + if ((flags >> 3) & 0x1) + printf("PSH,"); + if ((flags >> 2) & 0x1) + printf("RST,"); + if ((flags >> 1) & 0x1) + printf("SYN,"); + if (flags & 0x1) + printf("FIN,"); printf(")\n"); printf("Window Size: %" PRIu16 "\n", rte_be_to_cpu_16(hdr->rx_win)); @@ -272,8 +284,10 @@ onvm_pkt_print_ipv4(struct ipv4_hdr* hdr) { uint8_t flags = (hdr->fragment_offset >> 13) & 0b111; // there are three 1-bit flags, but only 2 are used printf("Flags: %" PRIx8 "\n", flags); printf("\t("); - if ((flags >> 1) & 0x1) printf("DF,"); - if ( flags & 0x1) printf("MF,"); + if ((flags >> 1) & 0x1) + printf("DF,"); + if (flags & 0x1) + printf("MF,"); printf("\n"); printf("Fragment Offset: %" PRIu16 "\n", rte_be_to_cpu_16(hdr->fragment_offset) & 0b1111111111111); @@ -292,25 +306,26 @@ onvm_pkt_print_ipv4(struct ipv4_hdr* hdr) { printf("Header Checksum: %" PRIu16 "\n", hdr->hdr_checksum); printf("Source IP: %" PRIu32 " (%" PRIu8 ".%" PRIu8 ".%" PRIu8 ".%" PRIu8 ")\n", hdr->src_addr, - hdr->src_addr & 0xFF, (hdr->src_addr >> 8) & 0xFF, (hdr->src_addr >> 16) & 0xFF, (hdr->src_addr >> 24) & 0xFF); + hdr->src_addr & 0xFF, (hdr->src_addr >> 8) & 0xFF, (hdr->src_addr >> 16) & 0xFF, + (hdr->src_addr >> 24) & 0xFF); printf("Destination IP: %" PRIu32 " (%" PRIu8 ".%" PRIu8 ".%" PRIu8 ".%" PRIu8 ")\n", hdr->dst_addr, - hdr->dst_addr & 0xFF, (hdr->dst_addr >> 8) & 0xFF, (hdr->dst_addr >> 16) & 0xFF, (hdr->dst_addr >> 24) & 0xFF); + hdr->dst_addr & 0xFF, (hdr->dst_addr >> 8) & 0xFF, (hdr->dst_addr >> 16) & 0xFF, + (hdr->dst_addr >> 24) & 0xFF); } -void onvm_pkt_print_ether(struct ether_hdr* hdr) { - const char *type = NULL; +void +onvm_pkt_print_ether(struct ether_hdr* hdr) { + const char* type = NULL; if (unlikely(hdr == NULL)) { return; } - printf("Source MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", - hdr->s_addr.addr_bytes[0], hdr->s_addr.addr_bytes[1], - hdr->s_addr.addr_bytes[2], hdr->s_addr.addr_bytes[3], - hdr->s_addr.addr_bytes[4], hdr->s_addr.addr_bytes[5]); - printf("Dest MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", - hdr->d_addr.addr_bytes[0], hdr->d_addr.addr_bytes[1], - hdr->d_addr.addr_bytes[2], hdr->d_addr.addr_bytes[3], - hdr->d_addr.addr_bytes[4], hdr->d_addr.addr_bytes[5]); - switch(hdr->ether_type) { + printf("Source MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", hdr->s_addr.addr_bytes[0], hdr->s_addr.addr_bytes[1], + hdr->s_addr.addr_bytes[2], hdr->s_addr.addr_bytes[3], hdr->s_addr.addr_bytes[4], + hdr->s_addr.addr_bytes[5]); + printf("Dest MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", hdr->d_addr.addr_bytes[0], hdr->d_addr.addr_bytes[1], + hdr->d_addr.addr_bytes[2], hdr->d_addr.addr_bytes[3], hdr->d_addr.addr_bytes[4], + hdr->d_addr.addr_bytes[5]); + switch (hdr->ether_type) { case ETHER_TYPE_IPv4: type = "IPv4"; break; @@ -343,7 +358,7 @@ void onvm_pkt_print_ether(struct ether_hdr* hdr) { } int -onvm_pkt_parse_ip(char *ip_str, uint32_t *dest) { +onvm_pkt_parse_ip(char* ip_str, uint32_t* dest) { int ret; int ip[4]; @@ -351,7 +366,7 @@ onvm_pkt_parse_ip(char *ip_str, uint32_t *dest) { return -1; } - ret = sscanf(ip_str, "%u.%u.%u.%u", &ip[3], &ip[2], &ip[1], &ip[0]); + ret = sscanf(ip_str, "%u.%u.%u.%u", &ip[0], &ip[1], &ip[2], &ip[3]); if (ret != 4) { return -1; } @@ -359,8 +374,14 @@ onvm_pkt_parse_ip(char *ip_str, uint32_t *dest) { return 0; } -int -onvm_pkt_parse_mac(char * mac_str, uint8_t* dest) { +void +onvm_pkt_parse_char_ip(char* ip_dest, uint32_t ip_src) { + snprintf(ip_dest, 16, "%u.%u.%u.%u", (ip_src >> 24) & 0xFF, (ip_src >> 16) & 0xFF, + (ip_src >> 8) & 0xFF, ip_src & 0xFF); +} + +int +onvm_pkt_parse_mac(char* mac_str, uint8_t* dest) { int ret, i; int mac[ETHER_ADDR_LEN]; @@ -373,7 +394,7 @@ onvm_pkt_parse_mac(char * mac_str, uint8_t* dest) { return -1; } - for (i = 0; i < ETHER_ADDR_LEN; i++){ + for (i = 0; i < ETHER_ADDR_LEN; i++) { dest[i] = mac[i]; } return 0; @@ -404,7 +425,7 @@ onvm_pkt_get_checksum_offload_flags(uint8_t port_id) { * except that this implementation can process packets with IP options. */ static uint16_t -calculate_tcpudp_cksum(const struct ipv4_hdr *ip, const void *l4_hdr, const uint32_t l3_len, uint8_t protocol) { +calculate_tcpudp_cksum(const struct ipv4_hdr* ip, const void* l4_hdr, const uint32_t l3_len, uint8_t protocol) { uint32_t cksum = 0; uint32_t l4_len = ip->total_length - l3_len; @@ -440,17 +461,17 @@ calculate_tcpudp_cksum(const struct ipv4_hdr *ip, const void *l4_hdr, const uint * exception that this implementation can process packets with IP options. */ static uint16_t -calculate_ip_cksum(const struct ipv4_hdr *ip, const uint32_t l3_len) { +calculate_ip_cksum(const struct ipv4_hdr* ip, const uint32_t l3_len) { uint16_t cksum = rte_raw_cksum(ip, l3_len); return (cksum == 0xffff) ? cksum : ~cksum; } void -onvm_pkt_set_checksums(struct rte_mbuf *pkt) { +onvm_pkt_set_checksums(struct rte_mbuf* pkt) { uint32_t hw_cksum_support = onvm_pkt_get_checksum_offload_flags(pkt->port); - struct ipv4_hdr *ip = onvm_pkt_ipv4_hdr(pkt); - struct tcp_hdr *tcp = onvm_pkt_tcp_hdr(pkt); - struct udp_hdr *udp = onvm_pkt_udp_hdr(pkt); + struct ipv4_hdr* ip = onvm_pkt_ipv4_hdr(pkt); + struct tcp_hdr* tcp = onvm_pkt_tcp_hdr(pkt); + struct udp_hdr* udp = onvm_pkt_udp_hdr(pkt); if (ip != NULL) { ip->hdr_checksum = 0; @@ -492,3 +513,244 @@ onvm_pkt_set_checksums(struct rte_mbuf *pkt) { } } } + +int +onvm_pkt_swap_ether_hdr(struct ether_hdr* ether_hdr) { + int i; + struct ether_addr temp_ether_addr; + + for (i = 0; i < ETHER_ADDR_LEN; ++i) { + temp_ether_addr.addr_bytes[i] = ether_hdr->s_addr.addr_bytes[i]; + ether_hdr->s_addr.addr_bytes[i] = ether_hdr->d_addr.addr_bytes[i]; + } + + for (i = 0; i < ETHER_ADDR_LEN; ++i) { + ether_hdr->d_addr.addr_bytes[i] = temp_ether_addr.addr_bytes[i]; + } + + return 0; +} + +int +onvm_pkt_swap_ip_hdr(struct ipv4_hdr* ip_hdr) { + uint32_t temp_ip; + + temp_ip = ip_hdr->src_addr; + ip_hdr->src_addr = ip_hdr->dst_addr; + ip_hdr->dst_addr = temp_ip; + + return 0; +} + +int +onvm_pkt_swap_tcp_hdr(struct tcp_hdr* tcp_hdr) { + uint16_t temp_port; + + temp_port = tcp_hdr->src_port; + tcp_hdr->src_port = tcp_hdr->dst_port; + tcp_hdr->dst_port = temp_port; + + return 0; +} + +struct rte_mbuf* +onvm_pkt_generate_tcp(struct rte_mempool* pktmbuf_pool, struct tcp_hdr* tcp_hdr, struct ipv4_hdr* iph, + struct ether_hdr* eth_hdr, uint8_t* options, size_t option_len, uint8_t* payload, + size_t payload_len) { + struct rte_mbuf* pkt; + uint8_t* pkt_payload; + uint8_t* tcp_options; + struct tcp_hdr* pkt_tcp_hdr; + struct ipv4_hdr* pkt_iph; + struct ether_hdr* pkt_eth_hdr; + + printf("Forming TCP packet, option_len %zu, payload_len %zu\n", option_len, payload_len); + + pkt = rte_pktmbuf_alloc(pktmbuf_pool); + if (pkt == NULL) { + return NULL; + } + + pkt->ol_flags = PKT_TX_IP_CKSUM | PKT_TX_IPV4 | PKT_TX_TCP_CKSUM; + pkt->l2_len = sizeof(struct ether_hdr); + pkt->l3_len = sizeof(struct ipv4_hdr); + + if (payload_len > 0) { + /* Set payload data */ + pkt_payload = (uint8_t*)rte_pktmbuf_prepend(pkt, payload_len); + if (pkt_payload == NULL) { + printf("Failed to prepend data. Consider splitting up the packet.\n"); + return NULL; + } + rte_memcpy(pkt_payload, payload, payload_len); + } + + if (option_len > 0) { + /* Set payload data */ + tcp_options = (uint8_t*)rte_pktmbuf_prepend(pkt, option_len); + if (tcp_options == NULL) { + printf("Failed to prepend data. Consider splitting up the packet.\n"); + return NULL; + } + rte_memcpy(tcp_options, options, option_len); + } + + /* Set tcp hdr */ + printf("TCP SIZE -> %lu\n", sizeof(*tcp_hdr)); + pkt_tcp_hdr = (struct tcp_hdr*)rte_pktmbuf_prepend(pkt, sizeof(*tcp_hdr)); + if (pkt_tcp_hdr == NULL) { + printf("Failed to prepend data. Consider splitting up the packet.\n"); + return NULL; + } + rte_memcpy(pkt_tcp_hdr, tcp_hdr, sizeof(*tcp_hdr)); // + option_len); + + /* Set ip hdr */ + pkt_iph = (struct ipv4_hdr*)rte_pktmbuf_prepend(pkt, sizeof(*iph)); + if (pkt_iph == NULL) { + printf("Failed to prepend data. Consider splitting up the packet.\n"); + return NULL; + } + rte_memcpy(pkt_iph, iph, sizeof(*iph)); + + /* Set eth hdr */ + pkt_eth_hdr = (struct ether_hdr*)rte_pktmbuf_prepend(pkt, sizeof(*eth_hdr)); + if (pkt_eth_hdr == NULL) { + printf("Failed to prepend data. Consider splitting up the packet.\n"); + return NULL; + } + rte_memcpy(pkt_eth_hdr, eth_hdr, sizeof(*eth_hdr)); + + pkt->pkt_len = pkt->data_len; + iph->total_length = rte_cpu_to_be_16(payload_len + option_len + sizeof(struct tcp_hdr) + + sizeof(struct ipv4_hdr) - sizeof(struct ether_hdr)); + printf("Pkt len %d, total iph len %lu\n", pkt->pkt_len, + payload_len + option_len + sizeof(struct tcp_hdr) + sizeof(struct ipv4_hdr) - sizeof(struct ether_hdr)); + + /* Handle checksuming */ + onvm_pkt_set_checksums(pkt); + + return pkt; +} + +int +onvm_pkt_fill_udp(struct udp_hdr* udp_hdr, uint16_t src_port, uint16_t dst_port, uint16_t payload_len) { + udp_hdr->src_port = rte_cpu_to_be_16(src_port); + udp_hdr->dst_port = rte_cpu_to_be_16(dst_port); + udp_hdr->dgram_cksum = 0; + udp_hdr->dgram_len = rte_cpu_to_be_16(payload_len + sizeof(struct udp_hdr)); + + return 0; +} + +int +onvm_pkt_fill_ipv4(struct ipv4_hdr* iph, uint32_t src, uint32_t dst, uint8_t l4_proto) { + iph->src_addr = rte_cpu_to_be_32(src); + iph->dst_addr = rte_cpu_to_be_32(dst); + iph->next_proto_id = l4_proto; + iph->version_ihl = IPV4_VERSION_IHL; + iph->time_to_live = IPV4_TTL; + iph->hdr_checksum = 0; + + return 0; +} + +int +onvm_pkt_fill_ether(struct ether_hdr* eth_hdr, int port, struct ether_addr* dst_mac_addr, struct port_info* ports) { + int i; + + /* Set ether header */ + ether_addr_copy(&ports->mac[port], ð_hdr->s_addr); + eth_hdr->ether_type = rte_be_to_cpu_16(ETHER_TYPE_IPv4); + for (i = 0; i < ETHER_ADDR_LEN; ++i) { + eth_hdr->d_addr.addr_bytes[i] = dst_mac_addr->addr_bytes[i]; + } + + return 0; +} + +struct rte_mbuf* +onvm_pkt_generate_udp_sample(struct rte_mempool* pktmbuf_pool) { + struct onvm_pkt_meta* pmeta = NULL; + struct rte_mbuf* pkt; + struct udp_hdr udp_hdr; + struct ipv4_hdr iph; + struct ether_hdr eth_hdr; + struct ether_addr d_addr = {.addr_bytes = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}; + char sample_msg[32] = "UDP packet sent from ONVM."; + size_t payload_len = sizeof(sample_msg); + + onvm_pkt_fill_udp(&udp_hdr, UDP_SAMPLE_SRC_PORT, UDP_SAMPLE_DST_PORT, payload_len); + onvm_pkt_fill_ipv4(&iph, IPV4_SAMPLE_SRC, IPV4_SAMPLE_DST, PROTO_UDP); + onvm_pkt_fill_ether(ð_hdr, SAMPLE_NIC_PORT, &d_addr, ports); + + pkt = onvm_pkt_generate_udp(pktmbuf_pool, &udp_hdr, &iph, ð_hdr, (uint8_t*)sample_msg, payload_len); + if (pkt == NULL) { + return NULL; + } + + /* Set packet dest */ + pmeta = onvm_get_pkt_meta(pkt); + pmeta->destination = SAMPLE_NIC_PORT; + pmeta->action = ONVM_NF_ACTION_OUT; + + return pkt; +} + +struct rte_mbuf* +onvm_pkt_generate_udp(struct rte_mempool* pktmbuf_pool, struct udp_hdr* udp_hdr, struct ipv4_hdr* iph, + struct ether_hdr* eth_hdr, uint8_t* payload, size_t payload_len) { + struct rte_mbuf* pkt; + uint8_t* pkt_payload; + struct udp_hdr* pkt_udp_hdr; + struct ipv4_hdr* pkt_iph; + struct ether_hdr* pkt_eth_hdr; + + pkt = rte_pktmbuf_alloc(pktmbuf_pool); + if (pkt == NULL) { + return NULL; + } + + pkt->ol_flags = PKT_TX_IP_CKSUM | PKT_TX_IPV4 | PKT_TX_UDP_CKSUM; + pkt->l2_len = sizeof(struct ether_hdr); + pkt->l3_len = sizeof(struct ipv4_hdr); + + /* Set payload data */ + pkt_payload = (uint8_t*)rte_pktmbuf_prepend(pkt, payload_len); + if (pkt_payload == NULL) { + printf("Failed to prepend data. Consider splitting up the packet.\n"); + return NULL; + } + memcpy(pkt_payload, payload, payload_len); + + /* Set udp hdr */ + pkt_udp_hdr = (struct udp_hdr*)rte_pktmbuf_prepend(pkt, sizeof(*udp_hdr)); + if (pkt_udp_hdr == NULL) { + printf("Failed to prepend data. Consider splitting up the packet.\n"); + return NULL; + } + memcpy(pkt_udp_hdr, udp_hdr, sizeof(*udp_hdr)); + + /* Set ip hdr */ + pkt_iph = (struct ipv4_hdr*)rte_pktmbuf_prepend(pkt, sizeof(*iph)); + if (pkt_iph == NULL) { + printf("Failed to prepend data. Consider splitting up the packet.\n"); + return NULL; + } + memcpy(pkt_iph, iph, sizeof(*iph)); + + /* Set eth hdr */ + pkt_eth_hdr = (struct ether_hdr*)rte_pktmbuf_prepend(pkt, sizeof(*eth_hdr)); + if (pkt_eth_hdr == NULL) { + printf("Failed to prepend data. Consider splitting up the packet.\n"); + return NULL; + } + memcpy(pkt_eth_hdr, eth_hdr, sizeof(*eth_hdr)); + + pkt->pkt_len = pkt->data_len; + iph->total_length = rte_cpu_to_be_16(pkt->pkt_len - sizeof(struct ether_hdr)); + + /* Handle checksuming */ + onvm_pkt_set_checksums(pkt); + + return pkt; +} diff --git a/onvm/onvm_nflib/onvm_pkt_helper.h b/onvm/onvm_nflib/onvm_pkt_helper.h index 0ea395182..fa4b2495d 100644 --- a/onvm/onvm_nflib/onvm_pkt_helper.h +++ b/onvm/onvm_nflib/onvm_pkt_helper.h @@ -5,8 +5,8 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -42,6 +42,7 @@ #define _ONVM_PKT_HELPER_H_ #include +#include #include struct port_info; @@ -53,20 +54,29 @@ struct ipv4_hdr; #define IP_PROTOCOL_TCP 6 #define IP_PROTOCOL_UDP 17 -#define SUPPORTS_IPV4_CHECKSUM_OFFLOAD (1<<0) -#define SUPPORTS_TCP_CHECKSUM_OFFLOAD (1<<1) -#define SUPPORTS_UDP_CHECKSUM_OFFLOAD (1<<2) +#define SUPPORTS_IPV4_CHECKSUM_OFFLOAD (1 << 0) +#define SUPPORTS_TCP_CHECKSUM_OFFLOAD (1 << 1) +#define SUPPORTS_UDP_CHECKSUM_OFFLOAD (1 << 2) + +#define PROTO_UDP 0x11 +#define IPV4_VERSION_IHL 69 +#define IPV4_TTL 64 +#define UDP_SAMPLE_SRC_PORT 12345 +#define UDP_SAMPLE_DST_PORT 54321 +#define IPV4_SAMPLE_SRC (uint32_t) IPv4(10, 0, 0, 1) +#define IPV4_SAMPLE_DST (uint32_t) IPv4(10, 0, 0, 2) +#define SAMPLE_NIC_PORT 0 /* Returns the bitflags in the tcp header */ -#define ONVM_PKT_GET_FLAGS(tcp, flags) \ - do { \ +#define ONVM_PKT_GET_FLAGS(tcp, flags) \ + do { \ (flags) = (((tcp)->data_off << 8) | (tcp)->tcp_flags) & 0b111111111; \ } while (0) /* Sets the bitflags in the tcp header */ -#define ONVM_PKT_SET_FLAGS(tcp, flags) \ - do { \ - (tcp)->tcp_flags = (flags) & 0xFF; \ +#define ONVM_PKT_SET_FLAGS(tcp, flags) \ + do { \ + (tcp)->tcp_flags = (flags)&0xFF; \ (tcp)->data_off |= ((flags) >> 8) & 0x1; \ } while (0) @@ -75,19 +85,19 @@ struct ipv4_hdr; * source and destination port ID. */ int -onvm_pkt_set_mac_addr(struct rte_mbuf* pkt, unsigned src_port_id, unsigned dst_port_id, struct port_info *ports); +onvm_pkt_set_mac_addr(struct rte_mbuf* pkt, unsigned src_port_id, unsigned dst_port_id, struct port_info* ports); /** * Swap the source MAC address of a packet with a specified destination port's MAC address. */ int -onvm_pkt_swap_src_mac_addr(struct rte_mbuf* pkt, unsigned dst_port_id, struct port_info *ports); +onvm_pkt_swap_src_mac_addr(struct rte_mbuf* pkt, unsigned dst_port_id, struct port_info* ports); /** * Swap the destination MAC address of a packet with a specified source port's MAC address. */ int -onvm_pkt_swap_dst_mac_addr(struct rte_mbuf* pkt, unsigned src_port_id, struct port_info *ports); +onvm_pkt_swap_dst_mac_addr(struct rte_mbuf* pkt, unsigned src_port_id, struct port_info* ports); /** * Return a pointer to the tcp/udp/ip header in the packet, or NULL if not a TCP packet @@ -138,22 +148,87 @@ onvm_pkt_print_ether(struct ether_hdr* hdr); * Parsing ip addr of form X.X.X.X into decimal form */ int -onvm_pkt_parse_ip(char * ip_str, uint32_t* dest); +onvm_pkt_parse_ip(char* ip_str, uint32_t* dest); + +/** + * Parse uint32 IP into a string + */ +void +onvm_pkt_parse_char_ip(char* ip_dest, uint32_t ip_src); /** * Parsing mac addr of form xx:xx:xx:xx:xx:xx into dest array */ int -onvm_pkt_parse_mac(char * mac_str, uint8_t* dest); +onvm_pkt_parse_mac(char* mac_str, uint8_t* dest); /** * Packet checksum calculation routines */ - uint32_t onvm_pkt_get_checksum_offload_flags(uint8_t port_id); +/** + * Set the packet checksums for the passed in pkt + */ void onvm_pkt_set_checksums(struct rte_mbuf* pkt); +/** + * Fill the packet UDP header + */ +int +onvm_pkt_fill_udp(struct udp_hdr* udp_hdr, uint16_t src_port, uint16_t dst_port, uint16_t payload_len); + +/** + * Fill the packet IP header + */ +int +onvm_pkt_fill_ipv4(struct ipv4_hdr* iph, uint32_t src, uint32_t dst, uint8_t l4_proto); + +/** + * Fill the ether header + */ +int +onvm_pkt_fill_ether(struct ether_hdr* eth_hdr, int port, struct ether_addr* dst_mac_addr, struct port_info* ports); + +/** + * Swap the ether header values + */ +int +onvm_pkt_swap_ether_hdr(struct ether_hdr* ether_hdr); + +/** + * Generates a UDP packet with the provided values + */ +int +onvm_pkt_swap_ip_hdr(struct ipv4_hdr* ip_hdr); + +/** + * Generates a TCP packet with the provided values + */ +int +onvm_pkt_swap_tcp_hdr(struct tcp_hdr* tcp_hdr); + +/** + * Generates a UDP packet with the provided values + */ +struct rte_mbuf* +onvm_pkt_generate_tcp(struct rte_mempool* pktmbuf_pool, struct tcp_hdr* tcp_hdr, struct ipv4_hdr* iph, + struct ether_hdr* eth_hdr, uint8_t* options, size_t option_len, uint8_t* payload, + size_t payload_len); + +/** + * Generates a UDP packet with the provided values + */ +struct rte_mbuf* +onvm_pkt_generate_udp(struct rte_mempool* pktmbuf_pool, struct udp_hdr* udp_hdr, struct ipv4_hdr* iph, + struct ether_hdr* eth_hdr, uint8_t* payload, size_t payload_len); + +/** + * Generates a sample UDP packet + */ +struct rte_mbuf* +onvm_pkt_generate_udp_sample(struct rte_mempool* pktmbuf_pool); + #endif // _ONVM_PKT_HELPER_H_" diff --git a/onvm/onvm_nflib/onvm_sc_common.c b/onvm/onvm_nflib/onvm_sc_common.c index cb8af8917..6dcd506c4 100644 --- a/onvm/onvm_nflib/onvm_sc_common.c +++ b/onvm/onvm_nflib/onvm_sc_common.c @@ -5,8 +5,8 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,17 +38,17 @@ * onvm_sc_common.c - service functions for manager and NFs ********************************************************************/ -#include +#include "onvm_sc_common.h" #include +#include #include "onvm_common.h" -#include "onvm_sc_common.h" /*********************************Interfaces**********************************/ uint16_t onvm_sc_service_to_nf_map(uint16_t service_id, struct rte_mbuf *pkt) { if (!nf_per_service_count || !services) { - rte_exit(EXIT_FAILURE, "Failed to retrieve service information\n"); + rte_exit(EXIT_FAILURE, "Failed to retrieve service information\n"); } uint16_t num_nfs_available = nf_per_service_count[service_id]; @@ -65,37 +65,37 @@ onvm_sc_service_to_nf_map(uint16_t service_id, struct rte_mbuf *pkt) { int onvm_sc_append_entry(struct onvm_service_chain *chain, uint8_t action, uint16_t destination) { - int chain_length = chain->chain_length; + int chain_length = chain->chain_length; - if (unlikely(chain_length > ONVM_MAX_CHAIN_LENGTH)) { - return ENOSPC; - } - /*the first entry is reserved*/ - chain_length++; - (chain->chain_length)++; - chain->sc[chain_length].action = action; - chain->sc[chain_length].destination = destination; + if (unlikely(chain_length > ONVM_MAX_CHAIN_LENGTH)) { + return ENOSPC; + } + /*the first entry is reserved*/ + chain_length++; + (chain->chain_length)++; + chain->sc[chain_length].action = action; + chain->sc[chain_length].destination = destination; - return 0; + return 0; } int onvm_sc_set_entry(struct onvm_service_chain *chain, int entry, uint8_t action, uint16_t destination) { - if (unlikely(entry > chain->chain_length)) { - return -1; - } + if (unlikely(entry > chain->chain_length)) { + return -1; + } - chain->sc[entry].action = action; - chain->sc[entry].destination = destination; - return 0; + chain->sc[entry].action = action; + chain->sc[entry].destination = destination; + return 0; } void onvm_sc_print(struct onvm_service_chain *chain) { - int i; - for (i = 1; i <= chain->chain_length; i++) { - printf("cur_index:%d, action:%"PRIu8", destination:%"PRIu16"\n", - i, chain->sc[i].action, chain->sc[i].destination); - } - printf("\n"); + int i; + for (i = 1; i <= chain->chain_length; i++) { + printf("cur_index:%d, action:%" PRIu8 ", destination:%" PRIu16 "\n", i, chain->sc[i].action, + chain->sc[i].destination); + } + printf("\n"); } diff --git a/onvm/onvm_nflib/onvm_sc_common.h b/onvm/onvm_nflib/onvm_sc_common.h index 1777c4332..d0ed4a032 100644 --- a/onvm/onvm_nflib/onvm_sc_common.h +++ b/onvm/onvm_nflib/onvm_sc_common.h @@ -5,8 +5,8 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,8 +38,8 @@ * onvm_sc_common.h - service functions for manager and NFs ********************************************************************/ -#ifndef _SC_COMMON_H_ -#define _SC_COMMON_H_ +#ifndef _ONVM_SC_COMMON_H_ +#define _ONVM_SC_COMMON_H_ #include #include "onvm_common.h" @@ -52,15 +52,19 @@ extern uint16_t *nf_per_service_count; /********************************Interfaces***********************************/ -uint16_t -onvm_sc_service_to_nf_map(uint16_t service_id, struct rte_mbuf *pkt);/*, uint16_t *nf_per_service_count, uint16_t **services);*/ +uint16_t +onvm_sc_service_to_nf_map(uint16_t service_id, + struct rte_mbuf *pkt); /*, uint16_t *nf_per_service_count, uint16_t **services);*/ /* append a entry to serivce chain, 0 means appending successful, 1 means failed*/ -int onvm_sc_append_entry(struct onvm_service_chain *chain, uint8_t action, uint16_t destination); +int +onvm_sc_append_entry(struct onvm_service_chain *chain, uint8_t action, uint16_t destination); /*set entry to a new action and destination, 0 means setting successful, 1 means failed */ -int onvm_sc_set_entry(struct onvm_service_chain *chain, int entry, uint8_t action, uint16_t destination); +int +onvm_sc_set_entry(struct onvm_service_chain *chain, int entry, uint8_t action, uint16_t destination); -void onvm_sc_print(struct onvm_service_chain *chain); +void +onvm_sc_print(struct onvm_service_chain *chain); -#endif //_SC_COMMON_H_ +#endif // _ONVM_SC_COMMON_H_ diff --git a/onvm/onvm_nflib/onvm_sc_mgr.c b/onvm/onvm_nflib/onvm_sc_mgr.c index 527848961..1aeabd7cd 100644 --- a/onvm/onvm_nflib/onvm_sc_mgr.c +++ b/onvm/onvm_nflib/onvm_sc_mgr.c @@ -5,8 +5,8 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,31 +35,29 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * onvm_sc_mgr.c - service chain functions for manager + * onvm_sc_mgr.c - service chain functions for manager ********************************************************************/ +#include "onvm_sc_mgr.h" #include -#include #include #include -#include "onvm_sc_mgr.h" +#include #include "onvm_sc_common.h" struct onvm_service_chain* onvm_sc_get(void) { - return NULL; + return NULL; } struct onvm_service_chain* -onvm_sc_create(void) -{ - struct onvm_service_chain *chain; +onvm_sc_create(void) { + struct onvm_service_chain* chain; - chain = rte_calloc("ONVM_sercice_chain", - 1, sizeof(struct onvm_service_chain), 0); + chain = rte_calloc("ONVM_sercice_chain", 1, sizeof(struct onvm_service_chain), 0); if (chain == NULL) { rte_exit(EXIT_FAILURE, "Cannot allocate memory for service chain\n"); } - return chain; + return chain; } diff --git a/onvm/onvm_nflib/onvm_sc_mgr.h b/onvm/onvm_nflib/onvm_sc_mgr.h index f3e73c170..57d1a1380 100644 --- a/onvm/onvm_nflib/onvm_sc_mgr.h +++ b/onvm/onvm_nflib/onvm_sc_mgr.h @@ -5,8 +5,8 @@ * BSD LICENSE * * Copyright(c) - * 2015-2017 George Washington University - * 2015-2017 University of California Riverside + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,40 +38,42 @@ * onvm_sc_mgr.h - service chain functions for manager ********************************************************************/ -#ifndef _SC_MGR_H_ -#define _SC_MGR_H_ +#ifndef _ONVM_SC_MGR_H_ +#define _ONVM_SC_MGR_H_ #include #include "onvm_common.h" static inline uint8_t onvm_next_action(struct onvm_service_chain* chain, uint16_t cur_nf) { - if (unlikely(cur_nf >= chain->chain_length)) { - return ONVM_NF_ACTION_DROP; - } - return chain->sc[cur_nf+1].action; + if (unlikely(cur_nf >= chain->chain_length)) { + return ONVM_NF_ACTION_DROP; + } + return chain->sc[cur_nf + 1].action; } static inline uint8_t onvm_sc_next_action(struct onvm_service_chain* chain, struct rte_mbuf* pkt) { - return onvm_next_action(chain, onvm_get_pkt_chain_index(pkt)); + return onvm_next_action(chain, onvm_get_pkt_chain_index(pkt)); } static inline uint16_t onvm_next_destination(struct onvm_service_chain* chain, uint16_t cur_nf) { - if (unlikely(cur_nf >= chain->chain_length)) { - return 0; - } - return chain->sc[cur_nf+1].destination; + if (unlikely(cur_nf >= chain->chain_length)) { + return 0; + } + return chain->sc[cur_nf + 1].destination; } static inline uint16_t onvm_sc_next_destination(struct onvm_service_chain* chain, struct rte_mbuf* pkt) { - return onvm_next_destination(chain, onvm_get_pkt_chain_index(pkt)); + return onvm_next_destination(chain, onvm_get_pkt_chain_index(pkt)); } /*get service chain*/ -struct onvm_service_chain* onvm_sc_get(void); +struct onvm_service_chain* +onvm_sc_get(void); /*create service chain*/ -struct onvm_service_chain* onvm_sc_create(void); -#endif // _SC_MGR_H_ +struct onvm_service_chain* +onvm_sc_create(void); +#endif // _ONVM_SC_MGR_H_ diff --git a/onvm/onvm_nflib/onvm_threading.c b/onvm/onvm_nflib/onvm_threading.c index 3bcab8707..f2aebd890 100644 --- a/onvm/onvm_nflib/onvm_threading.c +++ b/onvm/onvm_nflib/onvm_threading.c @@ -5,8 +5,8 @@ * BSD LICENSE * * Copyright(c) - * 2015-2018 George Washington University - * 2015-2018 University of California Riverside + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -42,19 +42,19 @@ #define _GNU_SOURCE #endif -#include -#include -#include -#include -#include #include #include #include +#include +#include +#include +#include +#include #include "onvm_threading.h" /*----------------------------------------------------------------------------*/ -int +int onvm_threading_get_num_cores(void) { return sysconf(_SC_NPROCESSORS_ONLN); } @@ -82,7 +82,7 @@ onvm_threading_get_core(uint16_t *core_value, uint8_t flags, struct core_status if (!ONVM_CHECK_BIT(flags, SHARE_CORE_BIT)) { if (cores[pref_core_id].nf_count == 0) cores[pref_core_id].is_dedicated_core = 1; - else + else return NF_NO_DEDICATED_CORES; } @@ -122,13 +122,13 @@ onvm_threading_get_core(uint16_t *core_value, uint8_t flags, struct core_status return 0; } -int +int onvm_threading_core_affinitize(int cpu) { cpu_set_t cpus; size_t n; n = onvm_threading_get_num_cores(); - if (cpu < 0 || cpu >= (int) n) { + if (cpu < 0 || cpu >= (int)n) { return -1; } diff --git a/onvm/onvm_nflib/onvm_threading.h b/onvm/onvm_nflib/onvm_threading.h index 92f151952..39b06ac63 100644 --- a/onvm/onvm_nflib/onvm_threading.h +++ b/onvm/onvm_nflib/onvm_threading.h @@ -5,8 +5,8 @@ * BSD LICENSE * * Copyright(c) - * 2015-2018 George Washington University - * 2015-2018 University of California Riverside + * 2015-2019 George Washington University + * 2015-2019 University of California Riverside * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -46,11 +46,12 @@ /** * Gets the total number of cores. */ -int onvm_threading_get_num_cores(void); +int +onvm_threading_get_num_cores(void); /** * Get the core id for a new NF to run on. - * If no flags are passed finds the core with the least number of NFs running on it, + * If no flags are passed finds the core with the least number of NFs running on it, * will reserve the core unless the share core flag is set * For manual core assignment: the user picks the core * For shared core will allow other NFs to share assigned core @@ -61,7 +62,7 @@ int onvm_threading_get_num_cores(void); * @param flags * Flags is a bitmask for specific core assignment options * Bit MANUAL_CORE_ASSIGNMENT_BIT: for manually choosing a core by the user - * Bit SHARE_CORE_BIT: allow other NFs(also with SHARE_CORE_BIT enabled) to start on assigned core + * Bit SHARE_CORE_BIT: allow other NFs(also with SHARE_CORE_BIT enabled) to start on assigned core * @param cores * A pointer to the core_status map containing core information * @@ -69,7 +70,8 @@ int onvm_threading_get_num_cores(void); * 0 on success * NF_NO_CORES or NF_NO_DEDICATED_CORES or NF_CORE_OUT_OF_RANGE or NF_CORE_BUSY on error */ -int onvm_threading_get_core(uint16_t *core_value, uint8_t flags, struct core_status *cores); +int +onvm_threading_get_core(uint16_t *core_value, uint8_t flags, struct core_status *cores); /** * Uses the dpdk function to reaffinitize the calling pthread to another core. @@ -79,6 +81,7 @@ int onvm_threading_get_core(uint16_t *core_value, uint8_t flags, struct core_sta * @return * 0 on success, or a negative value on failure */ -int onvm_threading_core_affinitize(int core); +int +onvm_threading_core_affinitize(int core); -#endif // _ONVM_THREADING_H_" +#endif // _ONVM_THREADING_H_ diff --git a/onvm_web/README.md b/onvm_web/README.md index 36c78d5fc..4ffaf243d 100644 --- a/onvm_web/README.md +++ b/onvm_web/README.md @@ -63,6 +63,34 @@ Global state is managed via a small publisher/subscriber library located in [pub **Please note:** If you restart the manager then you will need to refresh the web page to reset the application state. +## Build Installs + +The ReactJS app is ready for execution, but follow this process if you need to edit `onvm_web/react-app` +Assuming you're using an ubuntu system +Remember to run commands in sudo for nodejs and npm + +Install latest stable Node.js version +```sh +curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash - +# Install Node.js from the Debian-based distributions repository +sudo apt-get install -y nodejs +# Use package manager +sudo npm cache clean -f +sudo npm install -g n +sudo n stable +``` + +Install yarn with npm +```sh +sudo npm install -g yarn +``` + +Install programs to minify web files +```sh +sudo npm install uglify-js -g +sudo npm install uglifycss -g +``` + [install]: ../docs/Install.md [examples]: ../docs/Examples.md [start_web]: ./start_web_console.sh diff --git a/onvm_web/build_web.sh b/onvm_web/build_web.sh index f6ffeaff6..57a108f8f 100755 --- a/onvm_web/build_web.sh +++ b/onvm_web/build_web.sh @@ -47,3 +47,14 @@ yarn build cd ../ rm -rf ./web-build cp -r react-app/build ./web-build + +# NOTE: minification assumes uglifycss and uglify-js are installed + +echo "Minifying web files" +for css in ./web-build/static/css/*.css; do + uglifycss --output $css $css +done + +for js in $(find ./web-build -name '*.js'); do + uglifyjs --output $js $js +done \ No newline at end of file diff --git a/onvm_web/react-app/src/components/CoreCard.react.js b/onvm_web/react-app/src/components/CoreCard.react.js new file mode 100644 index 000000000..b00157754 --- /dev/null +++ b/onvm_web/react-app/src/components/CoreCard.react.js @@ -0,0 +1,26 @@ +// @flow + +import * as React from "react"; + +import { Card } from "tabler-react"; + +type Props = {| + coreLabel: string, + history?: Object, // react router history object + extraContent?: React.Node +|}; + +function CoreCard(props: Props): React.Node { + return ( + + + Core {props.coreLabel} + + + {props.extraContent} + + + ); +} + +export default CoreCard; diff --git a/onvm_web/react-app/src/components/NFCoreList.react.js b/onvm_web/react-app/src/components/NFCoreList.react.js new file mode 100644 index 000000000..d066bb16d --- /dev/null +++ b/onvm_web/react-app/src/components/NFCoreList.react.js @@ -0,0 +1,51 @@ +// @flow + +import * as React from "react"; + +import { Table, Button } from "tabler-react"; +import type { OnvmEvent } from "../pubsub"; + +type Props = {| + +sources: Array +|}; + +function NFCoreList(props: Props): React.Node { + return ( + + + {props.sources.map(source => { + if(!source.type) + return ( + {`${source.msg.split(" ")[0]} Thread`} + ); + + return + {`${source.type} - ${source.instance_id}`} + + ; + })} + +
+ ); +} + +export default NFCoreList; diff --git a/onvm_web/react-app/src/components/NFGraph.react.js b/onvm_web/react-app/src/components/NFGraph.react.js index d3c3822f6..57a2bf6a4 100644 --- a/onvm_web/react-app/src/components/NFGraph.react.js +++ b/onvm_web/react-app/src/components/NFGraph.react.js @@ -86,8 +86,11 @@ class NFGraph extends React.PureComponent { componentDidMount(): void { console.log("Graph Mount: " + this.props.nfLabel); + var nfLabel = `NF ${this.props.nfLabel.split(" - ")[1]}`; + if(this.props.nfLabel.substring(0, 4) === "Port") + nfLabel = this.props.nfLabel; const columnStateToRestore = registerNFSubscriber( - this.props.nfLabel, + nfLabel, this.dataCallback ); if (columnStateToRestore) { @@ -100,8 +103,11 @@ class NFGraph extends React.PureComponent { componentWillUnmount(): void { console.log("Graph Unmount: " + this.props.nfLabel); + var nfLabel = `NF ${this.props.nfLabel.split(" - ")[1]}`; + if(this.props.nfLabel.substring(0, 4) === "Port") + nfLabel = this.props.nfLabel; unregisterNFSubscriber( - this.props.nfLabel, + nfLabel, this.dataCallback, this.state.graphData.columns ); diff --git a/onvm_web/react-app/src/components/NFInfo.react.js b/onvm_web/react-app/src/components/NFInfo.react.js index 43ee41fb5..17f9f8b6a 100644 --- a/onvm_web/react-app/src/components/NFInfo.react.js +++ b/onvm_web/react-app/src/components/NFInfo.react.js @@ -14,34 +14,37 @@ type Props = {| type State = {| instanceId: ?number, - serviceId: ?number + serviceId: ?number, + core: ?number |}; class NFInfo extends React.PureComponent { state = { instanceId: null, - serviceId: null + serviceId: null, + core: null }; dataCallback = (data: OnvmNfData, _: number): void => { this.setState({ instanceId: data.instance_id, - serviceId: data.service_id + serviceId: data.service_id, + core: data.core }); }; componentDidMount(): void { console.log("NF Info Mount: " + this.props.nfLabel); - registerNFSubscriber(this.props.nfLabel, this.dataCallback); + registerNFSubscriber(`NF ${this.props.nfLabel.split(" - ")[1]}`, this.dataCallback); } componentWillUnmount(): void { console.log("NF Info Unmount: " + this.props.nfLabel); - unregisterNFSubscriber(this.props.nfLabel, this.dataCallback, null); + unregisterNFSubscriber(`NF ${this.props.nfLabel.split(" - ")[1]}`, this.dataCallback, null); } render(): React.Node { - const { serviceId, instanceId } = this.state; + const { serviceId, instanceId, core } = this.state; return ( @@ -58,6 +61,13 @@ class NFInfo extends React.PureComponent { {instanceId} + + + Core + + {core} + +
); diff --git a/onvm_web/react-app/src/pages/CoreMappingsPage.react.js b/onvm_web/react-app/src/pages/CoreMappingsPage.react.js index d49f38a4c..4ac80051c 100644 --- a/onvm_web/react-app/src/pages/CoreMappingsPage.react.js +++ b/onvm_web/react-app/src/pages/CoreMappingsPage.react.js @@ -3,22 +3,79 @@ import * as React from "react"; import { Page, Grid } from "tabler-react"; +import CoreCard from "../components/CoreCard.react"; +import NFCoreList from "../components/NFCoreList.react"; import { registerEventSubscriber, unregisterEventSubscriber } from "../pubsub"; -type Props = {||}; +import type { OnvmEvent } from "../pubsub"; + +type Props = {| + +history: Object // passed in from React Router +|}; type State = {| - coreMappings: Array + coreList: Object |}; class CoreMappingsPage extends React.PureComponent { state = { - coreMappings: [] + coreList: {} }; eventHandler = (event: OnvmEvent): void => { - // TODO: implement when shared CPU is completed + const source = event.source; + + if (event.message === "NF Ready" || event.message.includes("Start")) { + this.setState(prevState => { + var cores = { ...prevState.coreList }; + var core = source.core; + source.msg = event.message; + + if (core in cores && cores[core].length){ + /* we already have the core */ + var id = source.instance_id; + if(id && id !== cores[core][0].instance_id) + /* make sure we don't have nf in list */ + cores[core].unshift(source); + } else + /* create new map entry for core */ + cores[core] = [source]; + + return { coreList: cores }; + }); + } + + if (event.message === "NF Stopping" || event.message.includes("End")) { + this.setState(prevState => { + var cores = { ...prevState.coreList }; + var core, instance_id = event.source.instance_id; + var done = false; + + function is_instance(item){ + done = (item.instance_id === instance_id); + return !done; + } + + /* Bad performance O(cores*coreLength), but can be optimized with core_num later */ + for (core in cores) { + if (cores.hasOwnProperty(core)) { + cores[core] = cores[core].filter(i => is_instance(i)); + if (done) + break; + } + } + + if (!done) + console.log("Nothing removed from list!"); + + if (cores[core].length === 0) + /* nothing running on core */ + delete cores[core]; + + return { coreList: cores }; + }); + } }; componentDidMount(): void { @@ -30,12 +87,35 @@ class CoreMappingsPage extends React.PureComponent { } render(): React.Node { - const { coreMappings } = this.state; + const { history } = this.props; + const { coreList } = this.state; + + function isEmpty(obj) { + for (var key in obj) { + if (obj.hasOwnProperty(key)) return false; + } + return true; + } + return ( - {coreMappings.length === 0 && - "Core Mapping Info coming soon, once shared CPU implementation is completed."} + {Object.keys(coreList).map(key => { + var core = coreList[key]; + if (core && core.length > 0) + return ( + + + } + /> + + ); + else return ""; + })} + {isEmpty(coreList) && "No Cores have Running NFs!"} ); diff --git a/onvm_web/react-app/src/pages/NFDashboardPage.react.js b/onvm_web/react-app/src/pages/NFDashboardPage.react.js index 198d5afb0..114299d38 100644 --- a/onvm_web/react-app/src/pages/NFDashboardPage.react.js +++ b/onvm_web/react-app/src/pages/NFDashboardPage.react.js @@ -23,19 +23,34 @@ class NFDashboardPage extends React.PureComponent { }; eventHandler = (event: OnvmEvent): void => { + var nfLabel; + console.log(event); if (event.message === "NF Ready") { - const nfLabel = `NF ${event.source.instance_id}`; + nfLabel = ` - ${event.source.instance_id}`; + + if(event.source.type === "NF") + nfLabel = 'NF' + nfLabel; + else + nfLabel = event.source.type + nfLabel; + this.setState(prevState => { return { nfLabelList: [nfLabel, ...prevState.nfLabelList] }; }); } if (event.message === "NF Stopping") { - const nfLabel = `NF ${event.source.instance_id}`; + nfLabel = ` - ${event.source.instance_id}`; + if(event.source.type === "NF") + nfLabel = 'NF' + nfLabel; + else + nfLabel = event.source.type + nfLabel; + console.log(nfLabel); this.setState(prevState => { + console.log(prevState.nfLabelList); const arr = [ - ...prevState.nfLabelList.filter(label => label !== nfLabel) + ...prevState.nfLabelList.filter(label => label.split(" - ")[1] !== nfLabel.split(" - ")[1]) ]; + console.log("end: " + prevState.nfLabelList); return { nfLabelList: arr }; }); } diff --git a/onvm_web/react-app/src/pages/SingleNFPage.react.js b/onvm_web/react-app/src/pages/SingleNFPage.react.js index f569a6003..96ea335e6 100644 --- a/onvm_web/react-app/src/pages/SingleNFPage.react.js +++ b/onvm_web/react-app/src/pages/SingleNFPage.react.js @@ -27,9 +27,9 @@ class SingleNFPage extends React.PureComponent { }; eventHandler = (event: OnvmEvent): void => { - const instanceID = parseInt(this.state.nfLabel.replace("NF ", "")); - console.log(event); - if (event.source.type === "NF" && event.source.instance_id === instanceID) { + const instanceID = parseInt(this.state.nfLabel.split(" - ")[1]); + const source = event.source; + if (source.type && source.type !== "MGR" && event.source.instance_id === instanceID) { this.setState(prevState => { return { eventList: [...prevState.eventList, event] }; }); diff --git a/onvm_web/react-app/src/pubsub.js b/onvm_web/react-app/src/pubsub.js index 380864b8e..67268c4ab 100644 --- a/onvm_web/react-app/src/pubsub.js +++ b/onvm_web/react-app/src/pubsub.js @@ -7,7 +7,8 @@ export type OnvmNfData = {| TX_Drop_Rate: number, RX_Drop_Rate: number, instance_id: number, - service_id: number + service_id: number, + core: number |}; export type ColumnRestoreData = Array>; @@ -25,7 +26,8 @@ type OnvmResponse = {| export type OnvmEvent = {| message: string, instance_id: number, - service_id: number + service_id: number, + core: number |}; type OnvmEventsResponse = Array; diff --git a/onvm_web/web-build/asset-manifest.json b/onvm_web/web-build/asset-manifest.json index 001c8fbea..3176ef9a5 100644 --- a/onvm_web/web-build/asset-manifest.json +++ b/onvm_web/web-build/asset-manifest.json @@ -1,16 +1,16 @@ { - "main.css": "/static/css/main.006d58a7.chunk.css", - "main.js": "/static/js/main.e7aa6cb0.chunk.js", - "main.js.map": "/static/js/main.e7aa6cb0.chunk.js.map", - "static/css/1.13813cb9.chunk.css": "/static/css/1.13813cb9.chunk.css", - "static/js/1.34246748.chunk.js": "/static/js/1.34246748.chunk.js", - "static/js/1.34246748.chunk.js.map": "/static/js/1.34246748.chunk.js.map", + "main.css": "/static/css/main.3a4efa10.chunk.css", + "main.js": "/static/js/main.cfac8984.chunk.js", + "main.js.map": "/static/js/main.cfac8984.chunk.js.map", + "static/css/1.fe79427a.chunk.css": "/static/css/1.fe79427a.chunk.css", + "static/js/1.5af065d1.chunk.js": "/static/js/1.5af065d1.chunk.js", + "static/js/1.5af065d1.chunk.js.map": "/static/js/1.5af065d1.chunk.js.map", "runtime~main.js": "/static/js/runtime~main.229c360f.js", "runtime~main.js.map": "/static/js/runtime~main.229c360f.js.map", - "static/media/Tabler.css": "/static/media/gb-wls.2831a6dd.svg", - "static/css/1.13813cb9.chunk.css.map": "/static/css/1.13813cb9.chunk.css.map", - "static/css/main.006d58a7.chunk.css.map": "/static/css/main.006d58a7.chunk.css.map", + "static/media/Tabler.css": "/static/media/do.d8ab6db9.svg", + "static/css/main.3a4efa10.chunk.css.map": "/static/css/main.3a4efa10.chunk.css.map", + "static/css/1.fe79427a.chunk.css.map": "/static/css/1.fe79427a.chunk.css.map", "index.html": "/index.html", - "precache-manifest.bb594bddcd5aa10ab1d2cea76bb313fb.js": "/precache-manifest.bb594bddcd5aa10ab1d2cea76bb313fb.js", + "precache-manifest.a7124342f5fb4923d927c4421cb76d27.js": "/precache-manifest.a7124342f5fb4923d927c4421cb76d27.js", "service-worker.js": "/service-worker.js" -} +} \ No newline at end of file diff --git a/onvm_web/web-build/index.html b/onvm_web/web-build/index.html index b7fa67ebf..72763bb71 100644 --- a/onvm_web/web-build/index.html +++ b/onvm_web/web-build/index.html @@ -1 +1 @@ -OpenNetVM
\ No newline at end of file +OpenNetVM
\ No newline at end of file diff --git a/onvm_web/web-build/precache-manifest.a7124342f5fb4923d927c4421cb76d27.js b/onvm_web/web-build/precache-manifest.a7124342f5fb4923d927c4421cb76d27.js new file mode 100644 index 000000000..87531ea6d --- /dev/null +++ b/onvm_web/web-build/precache-manifest.a7124342f5fb4923d927c4421cb76d27.js @@ -0,0 +1 @@ +self.__precacheManifest=[{revision:"e223cee52ee80138dfc25a1885c83186",url:"/static/media/zw.e223cee5.svg"},{revision:"cfac89843334fe4661ae",url:"/static/css/main.3a4efa10.chunk.css"},{revision:"d8ab6db91dc2db4b36c7b91bb1b4ebf6",url:"/static/media/do.d8ab6db9.svg"},{revision:"5af065d15337cea63a9a",url:"/static/js/1.5af065d1.chunk.js"},{revision:"229c360febb4351a89df",url:"/static/js/runtime~main.229c360f.js"},{revision:"f2a4363b794cd6532749f37aafcf80b2",url:"/static/media/aol-explorer.f2a4363b.svg"},{revision:"e1d3686c3d84d0a4de49cbfbcd51f5df",url:"/static/media/android-browser.e1d3686c.svg"},{revision:"2bbe801cebc095f84c7d92574ec7214d",url:"/static/media/chrome.2bbe801c.svg"},{revision:"870087fd8e511c411e5baed4005d5bb7",url:"/static/media/chromium.870087fd.svg"},{revision:"f66d5a061976c8b9bf6e852d9d1e2de7",url:"/static/media/dolphin.f66d5a06.svg"},{revision:"abda4ac163b5c9be7f993273f229b735",url:"/static/media/edge.abda4ac1.svg"},{revision:"df51f6f457a137ad3b3a4246639450f7",url:"/static/media/maxthon.df51f6f4.svg"},{revision:"57c3e539655a13da5d3207594f0b78ed",url:"/static/media/ie.57c3e539.svg"},{revision:"ead509aef9d7ac89dc55069b4c0acbfd",url:"/static/media/blackberry.ead509ae.svg"},{revision:"f64e67934b079414af1bf18158c582dd",url:"/static/media/netscape.f64e6793.svg"},{revision:"438992de4e83d2123b1d9cdf8bb4bd03",url:"/static/media/opera.438992de.svg"},{revision:"1751c6d6fbb09e086eaf46c3897268d7",url:"/static/media/sleipnir.1751c6d6.svg"},{revision:"f600350d98a0611ab69a971109c6903c",url:"/static/media/uc-browser.f600350d.svg"},{revision:"6b04dfda8b985d2bbe4dd4ca1eb81bf8",url:"/static/media/vivaldi.6b04dfda.svg"},{revision:"1f331bd9d935496c9d7466390edf3718",url:"/static/media/ae.1f331bd9.svg"},{revision:"7cb635f05c1ed0924a1316940bfb4726",url:"/static/media/ag.7cb635f0.svg"},{revision:"1c4942df0b4d72faad8672114454bb09",url:"/static/media/al.1c4942df.svg"},{revision:"af917f4b31ecb7cc539fd89144632782",url:"/static/media/am.af917f4b.svg"},{revision:"fd948d03374b46e183d93dbd3709705c",url:"/static/media/ao.fd948d03.svg"},{revision:"fb98f0e65ec013a1920944ecc3826df3",url:"/static/media/aq.fb98f0e6.svg"},{revision:"2ed2ee2a0b02519ffee732e3d9d6f9fb",url:"/static/media/ar.2ed2ee2a.svg"},{revision:"511e196f2b286fc220c83221b3bb8a01",url:"/static/media/at.511e196f.svg"},{revision:"b853c2ebc123eab5abe5e71a6b329863",url:"/static/media/au.b853c2eb.svg"},{revision:"3301f616c5f32ad07226366d2882d9cd",url:"/static/media/ax.3301f616.svg"},{revision:"a441d8da7d266c9b9d315d06ee5fb429",url:"/static/media/ba.a441d8da.svg"},{revision:"ba2d1e5e6f743781569d6b671077db3c",url:"/static/media/az.ba2d1e5e.svg"},{revision:"c568edd5a2552c5cd6ce15009b8df3e2",url:"/static/media/bb.c568edd5.svg"},{revision:"b12e306073e83e7fec9d4b20e676b759",url:"/static/media/bd.b12e3060.svg"},{revision:"fb18617cede15ad0122419d7f1c74a9c",url:"/static/media/be.fb18617c.svg"},{revision:"f88288fa14a3979b34582b6018a62e35",url:"/static/media/bf.f88288fa.svg"},{revision:"bc04745d5b10f157bac0f3c721133844",url:"/static/media/bg.bc04745d.svg"},{revision:"805f2682e67e457f15d50fb7576fd477",url:"/static/media/bh.805f2682.svg"},{revision:"bc8085f9a17e392f05c127dc50293578",url:"/static/media/bi.bc8085f9.svg"},{revision:"ea52986c752328b2ca8ce558cab8a98f",url:"/static/media/bj.ea52986c.svg"},{revision:"a5c508b2ab0cdb85d5f22d40d2c3b97b",url:"/static/media/bl.a5c508b2.svg"},{revision:"4cac15edfcf9d342d21a46e5ee9cb2c2",url:"/static/media/bq.4cac15ed.svg"},{revision:"421969c2bb5a12b8936eacce44a57b24",url:"/static/media/bs.421969c2.svg"},{revision:"58761e89669c1387b966f11c2c1ddca3",url:"/static/media/bv.58761e89.svg"},{revision:"8ecb0b8e0257d3d0654dc7efd84da4c5",url:"/static/media/bw.8ecb0b8e.svg"},{revision:"af259017cdf3bcf91fa79f3639fff3fc",url:"/static/media/ca.af259017.svg"},{revision:"ec7f382086e8fcb4ac6fb02d1c4117fd",url:"/static/media/cc.ec7f3820.svg"},{revision:"020e3d1ee345ac631a4b463b073b57b7",url:"/static/media/cd.020e3d1e.svg"},{revision:"f75250a7950f5fb23779d262fd0da81d",url:"/static/media/cf.f75250a7.svg"},{revision:"497d91d1318438d0b128595d371d351e",url:"/static/media/cg.497d91d1.svg"},{revision:"d51618940f7f8df58cae92f4acb930fd",url:"/static/media/ch.d5161894.svg"},{revision:"1334b221487d5b445f6802ddd024a5c8",url:"/static/media/ci.1334b221.svg"},{revision:"869edc7171502fe62f15951ab9ab51ea",url:"/static/media/ck.869edc71.svg"},{revision:"17f2e2c9827f7a093f9b94921ed16638",url:"/static/media/cm.17f2e2c9.svg"},{revision:"9d5227cbd7309dd8801518e8f3d9a7de",url:"/static/media/cl.9d5227cb.svg"},{revision:"c2814ac0b9d72075571409689b113eb9",url:"/static/media/cn.c2814ac0.svg"},{revision:"433d22ad5949ca9376e7fee2398cf908",url:"/static/media/co.433d22ad.svg"},{revision:"20a9e6bf3919e282db3bf53d0e7820e2",url:"/static/media/cr.20a9e6bf.svg"},{revision:"050058cb15f5c99a8710f96da1461357",url:"/static/media/cu.050058cb.svg"},{revision:"6b6994926cbbd25d691d0d029999492a",url:"/static/media/cv.6b699492.svg"},{revision:"07a0d3f998ff4c6e2213fb5a4863325b",url:"/static/media/cw.07a0d3f9.svg"},{revision:"5180dbe51ead0d4850f3cc2db94d4cf9",url:"/static/media/cx.5180dbe5.svg"},{revision:"6731f87258db1b3310d7c8b86efa97e6",url:"/static/media/cz.6731f872.svg"},{revision:"01e89f77d4cd293951a32164b25ced60",url:"/static/media/de.01e89f77.svg"},{revision:"f4c086cc100130afc0642dc7bf1357de",url:"/static/media/dj.f4c086cc.svg"},{revision:"44761537674d28555c1985159c2b2e88",url:"/static/media/dk.44761537.svg"},{revision:"333db1efebf7b037389acd563050c357",url:"/static/media/dz.333db1ef.svg"},{revision:"57f366b0b55cf2fa11e3154e0865daf9",url:"/static/media/ee.57f366b0.svg"},{revision:"e4f13505f93239f55cf1bf1615297172",url:"/static/media/eh.e4f13505.svg"},{revision:"70738db67657a95db078a19581859e69",url:"/static/media/er.70738db6.svg"},{revision:"31aa0fc0721b95431f4b0dda48307c50",url:"/static/media/et.31aa0fc0.svg"},{revision:"17beaf811c76ebaa6bcfdb8b5e8a7325",url:"/static/media/eu.17beaf81.svg"},{revision:"58bcc4aff2131cf9d6eee5e30ec6fd62",url:"/static/media/fi.58bcc4af.svg"},{revision:"2bd7d4dffe1fd474663f05041e95e46a",url:"/static/media/fm.2bd7d4df.svg"},{revision:"dc9ed815f9a4bc59036c5fb3ef3aecca",url:"/static/media/fo.dc9ed815.svg"},{revision:"33442fb979e8f4f40b093bb4d6a39a7e",url:"/static/media/ga.33442fb9.svg"},{revision:"a178bcfbbbc26cb995fa19241b7a12a2",url:"/static/media/fr.a178bcfb.svg"},{revision:"a933214c8977a7009219775519a584b4",url:"/static/media/gb-eng.a933214c.svg"},{revision:"772350bf81e7b44332b5585cd15dfa3c",url:"/static/media/gb-sct.772350bf.svg"},{revision:"91974b40e86c4b9db2b9bd6ec533bfa7",url:"/static/media/mozilla.91974b40.svg"},{revision:"e18a59539f200660f10252a72f54ae08",url:"/static/media/as.e18a5953.svg"},{revision:"dc91764d503d73913f176521a3303166",url:"/static/media/aw.dc91764d.svg"},{revision:"fc7b82902896e1b59928e29ac9b75914",url:"/static/media/br.fc7b8290.svg"},{revision:"6fd2caab051b0d3e51cf99d32c67bbf9",url:"/static/media/by.6fd2caab.svg"},{revision:"657ef7aa34eb389fbf5217f52522e07f",url:"/static/media/cy.657ef7aa.svg"},{revision:"07f2e96d05bbcbc1fa02e8d0678c598e",url:"/static/media/eg.07f2e96d.svg"},{revision:"23c5c7fa88ed9eea0cec48a03e97787e",url:"/static/media/camino.23c5c7fa.svg"},{revision:"51104069fc2f5264f3f1b6a020457dd6",url:"/static/media/bn.51104069.svg"},{revision:"89c91dc6769c54e9971dcda38bc747ef",url:"/static/media/dm.89c91dc6.svg"},{revision:"6339387e4c3410f4bceb103b8ac0b7ec",url:"/static/media/bm.6339387e.svg"},{revision:"ee79ab6acab3d05faeb0df7db2689a2e",url:"/static/media/safari.ee79ab6a.svg"},{revision:"a8df755f8fdc9111d7b737b33529db81",url:"/static/media/af.a8df755f.svg"},{revision:"39149c620356690eaf75a6a32dfba374",url:"/static/media/bt.39149c62.svg"},{revision:"918e57b82b2daae8608426807dc39b67",url:"/static/media/ec.918e57b8.svg"},{revision:"943d406aa047964df31a94fc5a5021bc",url:"/static/media/gb-nir.943d406a.svg"},{revision:"b1ddba6040fc69b7d37591ffb7012787",url:"/static/media/fj.b1ddba60.svg"},{revision:"5c64395d99f225e9c106c55c4c06ee69",url:"/static/media/fk.5c64395d.svg"},{revision:"928b5a4f69d8929b73041bdf2ca49770",url:"/static/media/ai.928b5a4f.svg"},{revision:"30f99f820aca3d60dd8ecf3d5ee75764",url:"/static/media/ad.30f99f82.svg"},{revision:"5f3974a30d3ead800491befb7af540a8",url:"/static/media/westernunion-dark.5f3974a3.svg"},{revision:"4082e1b1ac8f311463c064a0875a8e5a",url:"/static/media/westernunion.4082e1b1.svg"},{revision:"c77724f331e1053188a5aa0d796ffc3b",url:"/static/media/webmoney.c77724f3.svg"},{revision:"5c559c4c11d8fda02a9f9e86e1615b41",url:"/static/media/webmoney-dark.5c559c4c.svg"},{revision:"a09152e75acbfee13fe82e13c54a77ad",url:"/static/media/visa.a09152e7.svg"},{revision:"f6a55e1d4fc96499269717a964bc3984",url:"/static/media/visa-dark.f6a55e1d.svg"},{revision:"012caff4df8cce6f2ea751366a4d0804",url:"/static/media/verifone.012caff4.svg"},{revision:"e7b2a0bc53907540e752d6cfd9e95930",url:"/static/media/verifone-dark.e7b2a0bc.svg"},{revision:"77c6af283968828069b3720792640fa9",url:"/static/media/stripe.77c6af28.svg"},{revision:"025afc3556434d9a218b3de9ae6aab11",url:"/static/media/stripe-dark.025afc35.svg"},{revision:"54599ad9cc5b0c3afea5db6b3d996e32",url:"/static/media/switch-dark.54599ad9.svg"},{revision:"c1a0e47dde0e275f4284a1e5b07a9219",url:"/static/media/switch.c1a0e47d.svg"},{revision:"4db9c83cfd89dfc89536c33d2065ae16",url:"/static/media/square-dark.4db9c83c.svg"},{revision:"48f113984b06dd75617b37d6d764a02b",url:"/static/media/square.48f11398.svg"},{revision:"f7fcc525735b4166573bc49f7c418161",url:"/static/media/solo.f7fcc525.svg"},{revision:"17da28b916977064d74363481913b58b",url:"/static/media/solo-dark.17da28b9.svg"},{revision:"a1a4a38c94505ac4c80974b84591059e",url:"/static/media/skrill-dark.a1a4a38c.svg"},{revision:"b0d31271e85a4ee845ff91eeb2dc1ab4",url:"/static/media/skrill.b0d31271.svg"},{revision:"1560c69d3cf081291eb13f477dc9e043",url:"/static/media/sage-dark.1560c69d.svg"},{revision:"c962e60b37391f1d7dd0a0ffacad256b",url:"/static/media/sage.c962e60b.svg"},{revision:"057164517322929b8b277ef36a63da87",url:"/static/media/payza.05716451.svg"},{revision:"aaf8d63fe0f20e267e21c89f0824edbf",url:"/static/media/payza-dark.aaf8d63f.svg"},{revision:"ece9e63914c3f788968b357cf6189e95",url:"/static/media/payu.ece9e639.svg"},{revision:"80265cc7c79041d66e9437374b08894c",url:"/static/media/payu-dark.80265cc7.svg"},{revision:"aa9749d2dbfa5fce884c050157002e4f",url:"/static/media/paypal.aa9749d2.svg"},{revision:"2abbaed44b22cd9ad7e423e88e9640f7",url:"/static/media/paypal-dark.2abbaed4.svg"},{revision:"992480f1d3c42a07ddcc81ef819277d7",url:"/static/media/payone-dark.992480f1.svg"},{revision:"2c68e11e3f322e662dc62c4700d2e835",url:"/static/media/payone.2c68e11e.svg"},{revision:"6f9066168c1fdf21bb40228737af2d9b",url:"/static/media/paymill.6f906616.svg"},{revision:"d8737b880a495605fed0d53b1a17100c",url:"/static/media/paymill-dark.d8737b88.svg"},{revision:"46f8af3b7129313668e112509e361f0d",url:"/static/media/paybox.46f8af3b.svg"},{revision:"321bd555c37290b6a89acc1922a3e3ad",url:"/static/media/paybox-dark.321bd555.svg"},{revision:"72f763a2ab7a69dcd6f92a1f448ff251",url:"/static/media/okpay.72f763a2.svg"},{revision:"26eabf7a3b75ddbb402d926bb9510afa",url:"/static/media/okpay-dark.26eabf7a.svg"},{revision:"8832c251bab55b7228f17ad1dcd93bcd",url:"/static/media/ogone.8832c251.svg"},{revision:"5fa709fb52bd0947dc6ddd33eab567fc",url:"/static/media/ogone-dark.5fa709fb.svg"},{revision:"798e0b4b9b2b5b2a6966e3160c8652d1",url:"/static/media/neteller.798e0b4b.svg"},{revision:"63736caca924eb35fb9104d4f432cfb0",url:"/static/media/neteller-dark.63736cac.svg"},{revision:"7df16d088d2d3fafc742fc011ab39191",url:"/static/media/monero.7df16d08.svg"},{revision:"29d40dee70c67525aa54c6d462843f4a",url:"/static/media/monero-dark.29d40dee.svg"},{revision:"4642dfb3bacbec31479381e4800275b2",url:"/static/media/laser.4642dfb3.svg"},{revision:"758bd7b66e03b7b4f0feb8195ac30124",url:"/static/media/laser-dark.758bd7b6.svg"},{revision:"20a24d68389a7dfe17336496dc3e51b3",url:"/static/media/ingenico.20a24d68.svg"},{revision:"5bef38951708ad075ebcd89dbed8d8d9",url:"/static/media/ingenico-dark.5bef3895.svg"},{revision:"7337d9d063907f6fd8d49214982e18a6",url:"/static/media/giropay.7337d9d0.svg"},{revision:"ff3c753ae34a95d2b30a9089319f29aa",url:"/static/media/giropay-dark.ff3c753a.svg"},{revision:"862b611ad759b765022ea1cac513bbfa",url:"/static/media/ebay.862b611a.svg"},{revision:"bd7ccde1aba2b3502e43a20b0dad4152",url:"/static/media/ebay-dark.bd7ccde1.svg"},{revision:"d63620a3337795f043b232846be946f8",url:"/static/media/worldpay.d63620a3.svg"},{revision:"a99e6d1ce661b5ec0118fa5e211dbdb1",url:"/static/media/worldpay-dark.a99e6d1c.svg"},{revision:"285de38e64669e7d6fdb6b88092a7adb",url:"/static/media/unionpay.285de38e.svg"},{revision:"22beb1a2dc02dd5b8ecd72b776937af0",url:"/static/media/unionpay-dark.22beb1a2.svg"},{revision:"7a542b9ee5e6c96713e790bbd3854c85",url:"/static/media/ukash.7a542b9e.svg"},{revision:"89b7d2ae90e9df97aa9e3a9940bac2c1",url:"/static/media/ukash-dark.89b7d2ae.svg"},{revision:"2a87d23fcf628021ed81203dc2305938",url:"/static/media/shopify.2a87d23f.svg"},{revision:"937412fda731ef86a0a3658eb6b1044f",url:"/static/media/shopify-dark.937412fd.svg"},{revision:"44f32f32a552d578ccb68df55740c84b",url:"/static/media/ripple.44f32f32.svg"},{revision:"a741b2b1463ca0e5cc9fd430004319b2",url:"/static/media/ripple-dark.a741b2b1.svg"},{revision:"0db2bc557a5ea15b0ba7f83b463776d3",url:"/static/media/paysafecard.0db2bc55.svg"},{revision:"2a3832c3bea2d4ad9b01ea999cbea582",url:"/static/media/paysafecard-dark.2a3832c3.svg"},{revision:"8d95de50838be9eb99e9db6eb23a3610",url:"/static/media/payoneer-dark.8d95de50.svg"},{revision:"e460ab6b6da17bf959f8d123cfeb4e2e",url:"/static/media/payoneer.e460ab6b.svg"},{revision:"31a202b40107161647c50fac56384c29",url:"/static/media/maestro.31a202b4.svg"},{revision:"0d91ff8fa73e4822b3df8578f6f90708",url:"/static/media/maestro-dark.0d91ff8f.svg"},{revision:"c05b3bbaa7150d0b60d6dfa8c602f70f",url:"/static/media/klarna.c05b3bba.svg"},{revision:"3a666a1e1aeba0c533c35132129e65db",url:"/static/media/klarna-dark.3a666a1e.svg"},{revision:"7f0e39ad58186b6fdbe5878970192668",url:"/static/media/googlewallet.7f0e39ad.svg"},{revision:"7cbe03bef872c536d6dbaa1f274ae0dc",url:"/static/media/googlewallet-dark.7cbe03be.svg"},{revision:"36f577700982f8fb3542d92a6c362650",url:"/static/media/dwolla.36f57770.svg"},{revision:"ccae276756a625bc248c34c7c49ddcf4",url:"/static/media/dwolla-dark.ccae2767.svg"},{revision:"54d6e672e8609e0b77d49f18c06430c7",url:"/static/media/eway.54d6e672.svg"},{revision:"bbf15466f81b7a24e9cc9e9522a2a709",url:"/static/media/eway-dark.bbf15466.svg"},{revision:"e86e9bd2426bbbbe2bba12fb641c185c",url:"/static/media/bz.e86e9bd2.svg"},{revision:"2f4fe159d3189ca05916f3ad46cb1a6c",url:"/static/media/discover.2f4fe159.svg"},{revision:"2cf523cd335b115a5678b068b56c3011",url:"/static/media/feather-webfont.2cf523cd.woff"},{revision:"1f0c2c56a34c8dce6fdbeaa80579e2c4",url:"/static/media/verisign-dark.1f0c2c56.svg"},{revision:"3684cf8229ff28f3054fa1d2a6095077",url:"/static/media/verisign.3684cf82.svg"},{revision:"a6684d9315e2ded55b8ee33df8c370d5",url:"/static/media/mastercard.a6684d93.svg"},{revision:"b1695f2bf43376465adea7252ec7837f",url:"/static/media/mastercard-dark.b1695f2b.svg"},{revision:"00f5c21f4be89a46de82c69e6259781c",url:"/static/media/discover-dark.00f5c21f.svg"},{revision:"3834e619996af0dec773a242f6fbf77c",url:"/static/media/sepa-dark.3834e619.svg"},{revision:"45d27bde30e9dcbf03da95a54dbe5720",url:"/static/media/sepa.45d27bde.svg"},{revision:"37695b626fb35b01215987cd7865ca7b",url:"/static/media/directdebit.37695b62.svg"},{revision:"bf510996f9f817b97d4618a413373998",url:"/static/media/directdebit-dark.bf510996.svg"},{revision:"b60982772ca2538902574c9790def63b",url:"/static/media/coinkite.b6098277.svg"},{revision:"b8e9cbc7ac23b572497cd2115bcf71c6",url:"/static/media/feather-webfont.b8e9cbc7.ttf"},{revision:"f50deb17e6e13ff02fe1f4c149d3166c",url:"/static/media/coinkite-dark.f50deb17.svg"},{revision:"eb61d075dbf8722029027b09b39cc3a8",url:"/static/media/clickandbuy.eb61d075.svg"},{revision:"cc5143b2b877ad1f2a9d7ddde2e55dee",url:"/static/media/feather-webfont.cc5143b2.eot"},{revision:"f7d38984e9cfaa1bf3f98a0046862667",url:"/static/media/clickandbuy-dark.f7d38984.svg"},{revision:"983db5f2256f8e24e520ef7d1146ed3f",url:"/static/media/cirrus.983db5f2.svg"},{revision:"1ff3d3f0d176196bbd3aaf4a6ecf7dac",url:"/static/media/applepay.1ff3d3f0.svg"},{revision:"e044dbdb76e1805843ae429c3c16bdd9",url:"/static/media/applepay-dark.e044dbdb.svg"},{revision:"31580e28ff89814332255e3f3ad510d6",url:"/static/media/alipay.31580e28.svg"},{revision:"b6a651d2cd0063d0e83b505c40f24dd7",url:"/static/media/alipay-dark.b6a651d2.svg"},{revision:"e14c0f5e3d367693fa699906a02119c6",url:"/static/media/2checkout.e14c0f5e.svg"},{revision:"65d58d809466b33a779ff1b029046730",url:"/static/media/2checkout-dark.65d58d80.svg"},{revision:"cfac89843334fe4661ae",url:"/static/js/main.cfac8984.chunk.js"},{revision:"625866342c77dcf827cdc22d004c6227",url:"/static/media/zm.62586634.svg"},{revision:"d8ffed672eb363336a1ad1ad4dc965be",url:"/static/media/za.d8ffed67.svg"},{revision:"a2dc66505c31b7096ba48bac4557855c",url:"/static/media/yt.a2dc6650.svg"},{revision:"55897575e3e0001ebfb8dcfba390495d",url:"/static/media/ye.55897575.svg"},{revision:"23b64335ac552f3d33e7544da45a2508",url:"/static/media/ws.23b64335.svg"},{revision:"4b4f5462b60b559d729a55f8719cf005",url:"/static/media/wf.4b4f5462.svg"},{revision:"0b7571b87f2faaa3d8e3b5662636d574",url:"/static/media/vn.0b7571b8.svg"},{revision:"9a6c3abc25acb7444923135ab30b7cb9",url:"/static/media/vu.9a6c3abc.svg"},{revision:"6f48a1b9488fe66e13887fb43304c009",url:"/static/media/ve.6f48a1b9.svg"},{revision:"f3912357d0a5339a1f402efefc89a8e7",url:"/static/media/vc.f3912357.svg"},{revision:"791dfbdae7960b7482e949dfac7c829a",url:"/static/media/uz.791dfbda.svg"},{revision:"a7e91b404efc4ad91c1360efd8e9cb4a",url:"/static/media/uy.a7e91b40.svg"},{revision:"2382ea7ec7cc55bfe1cc7a3ea8326989",url:"/static/media/us.2382ea7e.svg"},{revision:"a1fa2de39f9fdbd1e48a965bf697d700",url:"/static/media/um.a1fa2de3.svg"},{revision:"1e070275fe2eb891e7a1b90ac3c3ee13",url:"/static/media/ug.1e070275.svg"},{revision:"d5c9c20a3cfbf0c135ea7d58d29684f5",url:"/static/media/tz.d5c9c20a.svg"},{revision:"acc88be0743859f3c1d499c3117cfdcd",url:"/static/media/ua.acc88be0.svg"},{revision:"7baefd1c21ecb97a0a48a0d738bf79dc",url:"/static/media/tw.7baefd1c.svg"},{revision:"1a077ad0ee7788a6a1688dbfc5c12526",url:"/static/media/tv.1a077ad0.svg"},{revision:"f09daa6dc55999ef79edf7d708ad1f90",url:"/static/media/tt.f09daa6d.svg"},{revision:"aabe02c21bdc96b4499f10c7ead37008",url:"/static/media/tr.aabe02c2.svg"},{revision:"fa884203b4e844943f89c290c02ea246",url:"/static/media/to.fa884203.svg"},{revision:"ef273685b23f3978caf97e7fb0b2ea9d",url:"/static/media/tn.ef273685.svg"},{revision:"f563fdae9a3ca98f28a3c4c03a6d766f",url:"/static/media/tl.f563fdae.svg"},{revision:"22d4831b30e7a7ffa78d23628db3bdab",url:"/static/media/tk.22d4831b.svg"},{revision:"243a362ebddb29c473ace764e5b11e6b",url:"/static/media/cirrus-dark.243a362e.svg"},{revision:"b6533ad31f2b20a30bba38b0f2de1d9b",url:"/static/media/tj.b6533ad3.svg"},{revision:"ffb94e65905ea7a299e8ee52944abef1",url:"/static/media/bitpay.ffb94e65.svg"},{revision:"502695871e6c9632d23ed1db99f4e102",url:"/static/media/th.50269587.svg"},{revision:"8c0a0fa2bc07c9102ff49218b0ca9145",url:"/static/media/bancontact.8c0a0fa2.svg"},{revision:"f86a15dac57d28c89e0b69ac3eee63f8",url:"/static/media/bitpay-dark.f86a15da.svg"},{revision:"6e78609075a295f1627cd785a2005837",url:"/static/media/bancontact-dark.6e786090.svg"},{revision:"b96ee5428e8c67d6b1fc8bf73925af34",url:"/static/media/tg.b96ee542.svg"},{revision:"079a252552085195fa1e74c55965d960",url:"/static/media/td.079a2525.svg"},{revision:"adc24fb28bb1688520b8ee3272929644",url:"/static/media/tf.adc24fb2.svg"},{revision:"b89abdaf46ce1b76d1f382de92ed7c0e",url:"/static/media/americanexpress.b89abdaf.svg"},{revision:"c2ea2d77ce452a928487e9d62737ad4c",url:"/static/media/americanexpress-dark.c2ea2d77.svg"},{revision:"5c500045ab6cd762cd5f9abd393c2577",url:"/static/media/amazon.5c500045.svg"},{revision:"b178a57fcddb6156a5ec639d1b5d5a24",url:"/static/media/amazon-dark.b178a57f.svg"},{revision:"0fedea0746db6aa80b93dc14293c1754",url:"/static/media/sy.0fedea07.svg"},{revision:"230410b519c6205157002ce21ff8d629",url:"/static/media/st.230410b5.svg"},{revision:"0c7c9ffcd96a318fe1ed195441a6c2a9",url:"/static/media/ss.0c7c9ffc.svg"},{revision:"3bdb1de25c626c766b62e2c1cca11ea9",url:"/static/media/so.3bdb1de2.svg"},{revision:"65cdb1de480732b66f6a3675f49f2596",url:"/static/media/sr.65cdb1de.svg"},{revision:"b3c0a20f217b35d1cf1111736130dac8",url:"/static/media/vi.b3c0a20f.svg"},{revision:"4dc603d122f3ede3b07bfb751ee3a59c",url:"/static/media/sn.4dc603d1.svg"},{revision:"835d44f65482fc4d92251cb9eba71fa2",url:"/static/media/sl.835d44f6.svg"},{revision:"f44daf851804e866328d76cdd0b99074",url:"/static/media/sk.f44daf85.svg"},{revision:"8331157c241082c3ad0f499b47737ac2",url:"/static/media/sj.8331157c.svg"},{revision:"72f83c2946a14767d764c53baca31a7b",url:"/static/media/si.72f83c29.svg"},{revision:"22b0739e53b62deb793917e7ba4c1892",url:"/static/media/sg.22b0739e.svg"},{revision:"22475f5224df5500aa75813ba7608a23",url:"/static/media/se.22475f52.svg"},{revision:"a14badd55e756d1248fb262f896a6a84",url:"/static/media/sd.a14badd5.svg"},{revision:"fdc11a48b5b254f92ffc220dc1935963",url:"/static/media/sc.fdc11a48.svg"},{revision:"115ce3e59fc48f4e9307e69329ed0a85",url:"/static/media/sb.115ce3e5.svg"},{revision:"d9ac7b6156a3498ad0fd300b98f2f605",url:"/static/media/bitcoin.d9ac7b61.svg"},{revision:"edaf60e16ce0cc50bf2d0b7a499036e4",url:"/static/media/bitcoin-dark.edaf60e1.svg"},{revision:"46fb809f4912001f48fdc2b878e80f17",url:"/static/media/rw.46fb809f.svg"},{revision:"517e32a1f8c51260abfd28e65123eac8",url:"/static/media/ru.517e32a1.svg"},{revision:"1ae99e458e6568a1297a512ae21b85ba",url:"/static/media/sz.1ae99e45.svg"},{revision:"552b5d9744e1cb43fe34d598cc391113",url:"/static/media/ro.552b5d97.svg"},{revision:"a2dc66505c31b7096ba48bac4557855c",url:"/static/media/re.a2dc6650.svg"},{revision:"e037fac507a72f0149673ba30202ad09",url:"/static/media/firefox.e037fac5.svg"},{revision:"c6ca5440228101c2b83b4eb312a94731",url:"/static/media/es.c6ca5440.svg"},{revision:"67b058aefae79a7a8273c3a3ece09dae",url:"/static/media/sa.67b058ae.svg"},{revision:"2f7d308e80bd8a87fa1d2c63aa74fc5a",url:"/static/media/tc.2f7d308e.svg"},{revision:"20a4d7413504b137c05f202bbf385e9b",url:"/static/media/qa.20a4d741.svg"},{revision:"d23d18072122ea995d7f4f4bea2300fe",url:"/static/media/sx.d23d1807.svg"},{revision:"1519b6c631d063c9e495cd9f3dfd0f66",url:"/static/media/un.1519b6c6.svg"},{revision:"f3eb4474892199b59c8ca7272069e6ba",url:"/static/media/sm.f3eb4474.svg"},{revision:"0557592eea5bfc7ac4a3e3d41bde1e1c",url:"/static/media/pw.0557592e.svg"},{revision:"45249b1dd66c3b8425f9ce67f014d9ee",url:"/static/media/dinersclub.45249b1d.svg"},{revision:"baff56e3fdcd57bc731c02c4878e7441",url:"/static/media/dinersclub-dark.baff56e3.svg"},{revision:"225ede3505309835812a31d8cd526332",url:"/static/media/ps.225ede35.svg"},{revision:"e489537c791c5a57f554f17b21b02868",url:"/static/media/pr.e489537c.svg"},{revision:"3b3121b285747fdd0ca17486e084c675",url:"/static/media/vg.3b3121b2.svg"},{revision:"e129260bc90ab03c1f3b9f5452e0d66c",url:"/static/media/pt.e129260b.svg"},{revision:"d2132088d8448cd731e7047c1e432bf2",url:"/static/media/tm.d2132088.svg"},{revision:"bf813bfe31876e1a07e61f7ecdafd5a6",url:"/static/media/pn.bf813bfe.svg"},{revision:"a2dc66505c31b7096ba48bac4557855c",url:"/static/media/pm.a2dc6650.svg"},{revision:"4a878d5b85f694202ec0ccd16510be9c",url:"/static/media/feather-webfont.4a878d5b.svg"},{revision:"2257cff690948088abf92a799e89544e",url:"/static/media/pl.2257cff6.svg"},{revision:"abc5b39643482e82cb856bf160fa50fe",url:"/static/media/py.abc5b396.svg"},{revision:"0726abdb26a803057f8e22205c03f172",url:"/static/media/sh.0726abdb.svg"},{revision:"db891066a9bf98fd99cfa111abe7d535",url:"/static/media/pk.db891066.svg"},{revision:"8b5fbe69f9da3819f4887f6a01b8648e",url:"/static/media/ph.8b5fbe69.svg"},{revision:"e444f903a3056c776d7eb977380fa0c6",url:"/static/media/pg.e444f903.svg"},{revision:"28a15c37093a6700fb9db6c92bb9f714",url:"/static/media/pf.28a15c37.svg"},{revision:"4cabbfc6b407981692d9a034c04e3395",url:"/static/media/pe.4cabbfc6.svg"},{revision:"910761356d647746a34206d23e138727",url:"/static/media/pa.91076135.svg"},{revision:"e6bfaa15b7678d8441d4106e06376792",url:"/static/media/nu.e6bfaa15.svg"},{revision:"03d7410ae73601f5ec7122019a2ab888",url:"/static/media/nz.03d7410a.svg"},{revision:"f2afa5b9c3bb5ff4eac025d6a9e3e5ff",url:"/static/media/nr.f2afa5b9.svg"},{revision:"e6de69465e5e1ec155356a0827683a8a",url:"/static/media/np.e6de6946.svg"},{revision:"8331157c241082c3ad0f499b47737ac2",url:"/static/media/no.8331157c.svg"},{revision:"de2a39a27acc28aebde8173acc4bdf6d",url:"/static/media/nl.de2a39a2.svg"},{revision:"2ddc320beac15d92ffece6345b604540",url:"/static/media/ng.2ddc320b.svg"},{revision:"bad21adca6cd1a7c0498752de207dcbd",url:"/static/media/ne.bad21adc.svg"},{revision:"a2dc66505c31b7096ba48bac4557855c",url:"/static/media/nc.a2dc6650.svg"},{revision:"f38aead1dd402abc43b2e0dddd08ae47",url:"/static/media/na.f38aead1.svg"},{revision:"cd1e97af5e343e6d7db5c8f8bbb40cac",url:"/static/media/mz.cd1e97af.svg"},{revision:"aae5bd9cefde01ece247f58bf89a825c",url:"/static/media/my.aae5bd9c.svg"},{revision:"5b33db847ef48920cfec09f0c2926e90",url:"/static/media/mw.5b33db84.svg"},{revision:"e343afe8028575ea736d2677db4f7744",url:"/static/media/mv.e343afe8.svg"},{revision:"974b9e6c380a062b6504150999965d5f",url:"/static/media/mu.974b9e6c.svg"},{revision:"6b3d082dde2cd6355e7dd6194b258da7",url:"/static/media/mr.6b3d082d.svg"},{revision:"4c4286cd431a0194e7d35bcc875537b7",url:"/static/media/mq.4c4286cd.svg"},{revision:"36f1d6f2d8b53af76065ce17e6189104",url:"/static/media/mo.36f1d6f2.svg"},{revision:"cfd48e450bb31f3dc56b78fdac465bc0",url:"/static/media/mn.cfd48e45.svg"},{revision:"e6d7c5a4187b1fd8ab643d0e5d2f5bd1",url:"/static/media/mm.e6d7c5a4.svg"},{revision:"be076fd925ea2dd5a74f6a552166ba71",url:"/static/media/ml.be076fd9.svg"},{revision:"e02afe0476bb357aebde18136fda06e0",url:"/static/media/bo.e02afe04.svg"},{revision:"29cb0cb257ce61901ab1d97c97200be9",url:"/static/media/mk.29cb0cb2.svg"},{revision:"a3bb001b15d05e4a8974729fa75f9247",url:"/static/media/mh.a3bb001b.svg"},{revision:"0c0da5f0631b226d95fd57929b9e4b4b",url:"/static/media/mg.0c0da5f0.svg"},{revision:"a178bcfbbbc26cb995fa19241b7a12a2",url:"/static/media/mf.a178bcfb.svg"},{revision:"4241d3ff964cfdb68da07bb0f78520f4",url:"/static/media/mc.4241d3ff.svg"},{revision:"8c27c49311f54ab8d011b8eacf6c63cb",url:"/static/media/ma.8c27c493.svg"},{revision:"ededce3248f5c7f3e52a48bcfa55ac01",url:"/static/media/ly.ededce32.svg"},{revision:"06956a1377123bf7bf98076217a07361",url:"/static/media/lu.06956a13.svg"},{revision:"83353fa9cde68c8e128f85724e743e75",url:"/static/media/lv.83353fa9.svg"},{revision:"700ddad000d732b2603dcde0195ea3e7",url:"/static/media/ls.700ddad0.svg"},{revision:"14b63eab7de31bd29ffcdc4002433cd6",url:"/static/media/lt.14b63eab.svg"},{revision:"5485e606cf2dcf18e30b88581f14a459",url:"/static/media/lr.5485e606.svg"},{revision:"6c2940dae95d15b98cf38bcf44816d21",url:"/static/media/lc.6c2940da.svg"},{revision:"fc2d0f07ea618d781e800bd8cd49d92c",url:"/static/media/nf.fc2d0f07.svg"},{revision:"4981974031355cb8cb9fa6ae351ec6cf",url:"/static/media/lb.49819740.svg"},{revision:"bdfc4ab5e964e3466fcf31b5ec4bf87b",url:"/static/media/la.bdfc4ab5.svg"},{revision:"cffcad7981a89128ffef6ec871c5ef96",url:"/static/media/mt.cffcad79.svg"},{revision:"8b73c710b4a9a2c91ed2683bd2ba2a41",url:"/static/media/ms.8b73c710.svg"},{revision:"3e24a94a1aee5cfa3c34f2fa6f8f1845",url:"/static/media/kw.3e24a94a.svg"},{revision:"32f23fafe64cce64d0e30c1d80e761ae",url:"/static/media/kr.32f23faf.svg"},{revision:"b2729dfae51752a2cb41de576c90b6bb",url:"/static/media/kp.b2729dfa.svg"},{revision:"7ab9462c3019492674aa27c5f42df7f1",url:"/static/media/kn.7ab9462c.svg"},{revision:"fbe824dcd1ef2519d2d21f189a345c2a",url:"/static/media/ki.fbe824dc.svg"},{revision:"cd351374021fde2537ae578691612f30",url:"/static/media/km.cd351374.svg"},{revision:"de33c0489053970bffc24559744aaae3",url:"/static/media/kg.de33c048.svg"},{revision:"15b698f31b8bec3028bea1726cea84fb",url:"/static/media/ke.15b698f3.svg"},{revision:"fd2646810e3b7a16d5ff0e16401fcf94",url:"/static/media/jp.fd264681.svg"},{revision:"f9aceffb03e9764fac60e5aafe3743ec",url:"/static/media/md.f9aceffb.svg"},{revision:"d14059401101d457efe14ba2495e69c6",url:"/static/media/jo.d1405940.svg"},{revision:"7db0ffd8c9e9717bf8a4e670b8e14de8",url:"/static/media/jm.7db0ffd8.svg"},{revision:"6a9e1b932b348bea888a9cb0a21ad581",url:"/static/media/je.6a9e1b93.svg"},{revision:"bd6b5ff3c79cb3d80d524f342ff99ba4",url:"/static/media/it.bd6b5ff3.svg"},{revision:"10e0d5b28508b7a92f02b01c8f54bfe7",url:"/static/media/li.10e0d5b2.svg"},{revision:"ec1fb8765fe74b0912ab152afe850c38",url:"/static/media/is.ec1fb876.svg"},{revision:"2646bc518e3540d4639365448d02b23d",url:"/static/media/jcb.2646bc51.svg"},{revision:"f9bf701dcacbc6a9e40cc626153d6ff9",url:"/static/media/jcb-dark.f9bf701d.svg"},{revision:"61fca1841f4f8e1b031eeeb7a7708650",url:"/static/media/iq.61fca184.svg"},{revision:"bfffb443939dc4de9a1926380b3c99b4",url:"/static/media/kh.bfffb443.svg"},{revision:"2d667fbb3870fa62aa27eece3a00196c",url:"/static/media/in.2d667fbb.svg"},{revision:"9b7a06b9a821841e7a5fd0f3e3ab8cc4",url:"/static/media/om.9b7a06b9.svg"},{revision:"f0a4f4f6d893038aa99ccbcb7f6e5271",url:"/static/media/lk.f0a4f4f6.svg"},{revision:"0ea7e9dad5f9fce9cdee314eea294da8",url:"/static/media/il.0ea7e9da.svg"},{revision:"2b983496dce81d0805a0d92443e8000c",url:"/static/media/ni.2b983496.svg"},{revision:"d609c4e7bbb267cc920b9bfacdf8c553",url:"/static/media/ie.d609c4e7.svg"},{revision:"529db212e9de897dc2dd42f4ad7f8fd3",url:"/static/media/kz.529db212.svg"},{revision:"ee020a0f5bc9d6586b97f9a9dfea47a0",url:"/static/media/id.ee020a0f.svg"},{revision:"a8abaf3779c44dbb5d3604b621d899fc",url:"/static/media/hu.a8abaf37.svg"},{revision:"3d726baafa62f8f9fee22363226fb75c",url:"/static/media/hn.3d726baa.svg"},{revision:"b43f38576524ed7038b5afd6337d5759",url:"/static/media/hm.b43f3857.svg"},{revision:"fb606eb1063380a1c9d858161cf5f0a7",url:"/static/media/hk.fb606eb1.svg"},{revision:"19884f0c27b6b1a57a12fdb7b682eed2",url:"/static/media/im.19884f0c.svg"},{revision:"19bcfc3477c49626f2f9e4291e3f81bd",url:"/static/media/gy.19bcfc34.svg"},{revision:"fcdc8e3981178bdf4bf5f382fa7e7dab",url:"/static/media/mp.fcdc8e39.svg"},{revision:"3cb275a7c517640ff251ce419ba5a7be",url:"/static/media/ir.3cb275a7.svg"},{revision:"e1d47aa4658950ee3f11d125f19a604a",url:"/static/media/gw.e1d47aa4.svg"},{revision:"f7c3a515e4c61b03f6818233ded0bd8c",url:"/static/media/ky.f7c3a515.svg"},{revision:"d0404e4a48a02f0e5b393e7f88d02648",url:"/static/media/ht.d0404e4a.svg"},{revision:"ad34e604908c0fd1e96af29a7e9b983f",url:"/static/media/gu.ad34e604.svg"},{revision:"9a9a62a1f4f53cc87d02925098293360",url:"/static/media/gr.9a9a62a1.svg"},{revision:"6bbb0e7695e648aa9d7e25eff7165284",url:"/static/media/gq.6bbb0e76.svg"},{revision:"a178bcfbbbc26cb995fa19241b7a12a2",url:"/static/media/gp.a178bcfb.svg"},{revision:"6b139c75ff4f94335205a2d93dc7e090",url:"/static/media/va.6b139c75.svg"},{revision:"9423800e095be53df9249808ce63306c",url:"/static/media/gm.9423800e.svg"},{revision:"e472dff761a5641c37c985858a735dc3",url:"/static/media/gn.e472dff7.svg"},{revision:"d02c42ea2b63c1131bb36da347ac3490",url:"/static/media/gl.d02c42ea.svg"},{revision:"2e0c61df4402b9748b394cf508f1a0c7",url:"/static/media/io.2e0c61df.svg"},{revision:"c9543d40b95a35ff339fe78d6184b6d1",url:"/static/media/gi.c9543d40.svg"},{revision:"d339aeb27fefd04b3c8238b7d8f26473",url:"/static/media/gg.d339aeb2.svg"},{revision:"d4b35e14b2cdd6bb630a7b2c8902d7b7",url:"/static/media/gh.d4b35e14.svg"},{revision:"4ea8e1590ad37f3d4fb8c58c7906a73c",url:"/static/media/gf.4ea8e159.svg"},{revision:"334a8275142fd63934abf3a8f8c5a913",url:"/static/media/ge.334a8275.svg"},{revision:"c17d779e8552e59c9ef032f0a432fcfb",url:"/static/media/gd.c17d779e.svg"},{revision:"5638bbd9874edd22c39b0c4a54b1de21",url:"/static/media/gb.5638bbd9.svg"},{revision:"2831a6dd51c5a036e31203cd6faef1f7",url:"/static/media/gb-wls.2831a6dd.svg"},{revision:"a21150d5864835c762dd3bdb21e61320",url:"/static/media/sv.a21150d5.svg"},{revision:"3721691749ae1da7133203472974ea5f",url:"/static/media/gs.37216917.svg"},{revision:"0b689ffe012a208dbd4609b8e7a6ce4c",url:"/static/media/gt.0b689ffe.svg"},{revision:"79e564a4cd82e29e24b5a3ff6c6d914e",url:"/static/media/hr.79e564a4.svg"},{revision:"399015d8b358e8c3c2c1a3e699752e63",url:"/static/media/me.399015d8.svg"},{revision:"426b1d470b7392ef3ea723342a138c6f",url:"/static/media/rs.426b1d47.svg"},{revision:"184d53d145cbbb2eafe2bc7a3bd66c62",url:"/static/media/mx.184d53d1.svg"},{revision:"5af065d15337cea63a9a",url:"/static/css/1.fe79427a.chunk.css"},{revision:"40931733e3a3a913ea43318315a67b6f",url:"/index.html"}]; \ No newline at end of file diff --git a/onvm_web/web-build/precache-manifest.bb594bddcd5aa10ab1d2cea76bb313fb.js b/onvm_web/web-build/precache-manifest.bb594bddcd5aa10ab1d2cea76bb313fb.js deleted file mode 100644 index d9a8dfb0a..000000000 --- a/onvm_web/web-build/precache-manifest.bb594bddcd5aa10ab1d2cea76bb313fb.js +++ /dev/null @@ -1,1566 +0,0 @@ -self.__precacheManifest = [ - { - revision: "eb61d075dbf8722029027b09b39cc3a8", - url: "/static/media/clickandbuy.eb61d075.svg" - }, - { - revision: "e7aa6cb086876090995d", - url: "/static/css/main.006d58a7.chunk.css" - }, - { - revision: "2831a6dd51c5a036e31203cd6faef1f7", - url: "/static/media/gb-wls.2831a6dd.svg" - }, - { - revision: "3424674880e35d2c3d70", - url: "/static/js/1.34246748.chunk.js" - }, - { - revision: "229c360febb4351a89df", - url: "/static/js/runtime~main.229c360f.js" - }, - { - revision: "e1d3686c3d84d0a4de49cbfbcd51f5df", - url: "/static/media/android-browser.e1d3686c.svg" - }, - { - revision: "f2a4363b794cd6532749f37aafcf80b2", - url: "/static/media/aol-explorer.f2a4363b.svg" - }, - { - revision: "2bbe801cebc095f84c7d92574ec7214d", - url: "/static/media/chrome.2bbe801c.svg" - }, - { - revision: "23c5c7fa88ed9eea0cec48a03e97787e", - url: "/static/media/camino.23c5c7fa.svg" - }, - { - revision: "870087fd8e511c411e5baed4005d5bb7", - url: "/static/media/chromium.870087fd.svg" - }, - { - revision: "ead509aef9d7ac89dc55069b4c0acbfd", - url: "/static/media/blackberry.ead509ae.svg" - }, - { - revision: "abda4ac163b5c9be7f993273f229b735", - url: "/static/media/edge.abda4ac1.svg" - }, - { - revision: "df51f6f457a137ad3b3a4246639450f7", - url: "/static/media/maxthon.df51f6f4.svg" - }, - { - revision: "57c3e539655a13da5d3207594f0b78ed", - url: "/static/media/ie.57c3e539.svg" - }, - { - revision: "91974b40e86c4b9db2b9bd6ec533bfa7", - url: "/static/media/mozilla.91974b40.svg" - }, - { - revision: "f66d5a061976c8b9bf6e852d9d1e2de7", - url: "/static/media/dolphin.f66d5a06.svg" - }, - { - revision: "438992de4e83d2123b1d9cdf8bb4bd03", - url: "/static/media/opera.438992de.svg" - }, - { - revision: "f64e67934b079414af1bf18158c582dd", - url: "/static/media/netscape.f64e6793.svg" - }, - { - revision: "ee79ab6acab3d05faeb0df7db2689a2e", - url: "/static/media/safari.ee79ab6a.svg" - }, - { - revision: "f600350d98a0611ab69a971109c6903c", - url: "/static/media/uc-browser.f600350d.svg" - }, - { - revision: "1751c6d6fbb09e086eaf46c3897268d7", - url: "/static/media/sleipnir.1751c6d6.svg" - }, - { - revision: "30f99f820aca3d60dd8ecf3d5ee75764", - url: "/static/media/ad.30f99f82.svg" - }, - { - revision: "6b04dfda8b985d2bbe4dd4ca1eb81bf8", - url: "/static/media/vivaldi.6b04dfda.svg" - }, - { - revision: "e037fac507a72f0149673ba30202ad09", - url: "/static/media/firefox.e037fac5.svg" - }, - { - revision: "1f331bd9d935496c9d7466390edf3718", - url: "/static/media/ae.1f331bd9.svg" - }, - { - revision: "7cb635f05c1ed0924a1316940bfb4726", - url: "/static/media/ag.7cb635f0.svg" - }, - { - revision: "a8df755f8fdc9111d7b737b33529db81", - url: "/static/media/af.a8df755f.svg" - }, - { - revision: "fd948d03374b46e183d93dbd3709705c", - url: "/static/media/ao.fd948d03.svg" - }, - { - revision: "1c4942df0b4d72faad8672114454bb09", - url: "/static/media/al.1c4942df.svg" - }, - { - revision: "af917f4b31ecb7cc539fd89144632782", - url: "/static/media/am.af917f4b.svg" - }, - { - revision: "928b5a4f69d8929b73041bdf2ca49770", - url: "/static/media/ai.928b5a4f.svg" - }, - { - revision: "2ed2ee2a0b02519ffee732e3d9d6f9fb", - url: "/static/media/ar.2ed2ee2a.svg" - }, - { - revision: "fb98f0e65ec013a1920944ecc3826df3", - url: "/static/media/aq.fb98f0e6.svg" - }, - { - revision: "511e196f2b286fc220c83221b3bb8a01", - url: "/static/media/at.511e196f.svg" - }, - { - revision: "e18a59539f200660f10252a72f54ae08", - url: "/static/media/as.e18a5953.svg" - }, - { - revision: "b853c2ebc123eab5abe5e71a6b329863", - url: "/static/media/au.b853c2eb.svg" - }, - { - revision: "dc91764d503d73913f176521a3303166", - url: "/static/media/aw.dc91764d.svg" - }, - { - revision: "ba2d1e5e6f743781569d6b671077db3c", - url: "/static/media/az.ba2d1e5e.svg" - }, - { - revision: "b12e306073e83e7fec9d4b20e676b759", - url: "/static/media/bd.b12e3060.svg" - }, - { - revision: "c568edd5a2552c5cd6ce15009b8df3e2", - url: "/static/media/bb.c568edd5.svg" - }, - { - revision: "a441d8da7d266c9b9d315d06ee5fb429", - url: "/static/media/ba.a441d8da.svg" - }, - { - revision: "3301f616c5f32ad07226366d2882d9cd", - url: "/static/media/ax.3301f616.svg" - }, - { - revision: "fb18617cede15ad0122419d7f1c74a9c", - url: "/static/media/be.fb18617c.svg" - }, - { - revision: "bc04745d5b10f157bac0f3c721133844", - url: "/static/media/bg.bc04745d.svg" - }, - { - revision: "f88288fa14a3979b34582b6018a62e35", - url: "/static/media/bf.f88288fa.svg" - }, - { - revision: "bc8085f9a17e392f05c127dc50293578", - url: "/static/media/bi.bc8085f9.svg" - }, - { - revision: "805f2682e67e457f15d50fb7576fd477", - url: "/static/media/bh.805f2682.svg" - }, - { - revision: "ea52986c752328b2ca8ce558cab8a98f", - url: "/static/media/bj.ea52986c.svg" - }, - { - revision: "a5c508b2ab0cdb85d5f22d40d2c3b97b", - url: "/static/media/bl.a5c508b2.svg" - }, - { - revision: "6339387e4c3410f4bceb103b8ac0b7ec", - url: "/static/media/bm.6339387e.svg" - }, - { - revision: "4cac15edfcf9d342d21a46e5ee9cb2c2", - url: "/static/media/bq.4cac15ed.svg" - }, - { - revision: "51104069fc2f5264f3f1b6a020457dd6", - url: "/static/media/bn.51104069.svg" - }, - { - revision: "fc7b82902896e1b59928e29ac9b75914", - url: "/static/media/br.fc7b8290.svg" - }, - { - revision: "58761e89669c1387b966f11c2c1ddca3", - url: "/static/media/bv.58761e89.svg" - }, - { - revision: "39149c620356690eaf75a6a32dfba374", - url: "/static/media/bt.39149c62.svg" - }, - { - revision: "e02afe0476bb357aebde18136fda06e0", - url: "/static/media/bo.e02afe04.svg" - }, - { - revision: "6fd2caab051b0d3e51cf99d32c67bbf9", - url: "/static/media/by.6fd2caab.svg" - }, - { - revision: "421969c2bb5a12b8936eacce44a57b24", - url: "/static/media/bs.421969c2.svg" - }, - { - revision: "8ecb0b8e0257d3d0654dc7efd84da4c5", - url: "/static/media/bw.8ecb0b8e.svg" - }, - { - revision: "af259017cdf3bcf91fa79f3639fff3fc", - url: "/static/media/ca.af259017.svg" - }, - { - revision: "ec7f382086e8fcb4ac6fb02d1c4117fd", - url: "/static/media/cc.ec7f3820.svg" - }, - { - revision: "f75250a7950f5fb23779d262fd0da81d", - url: "/static/media/cf.f75250a7.svg" - }, - { - revision: "e86e9bd2426bbbbe2bba12fb641c185c", - url: "/static/media/bz.e86e9bd2.svg" - }, - { - revision: "020e3d1ee345ac631a4b463b073b57b7", - url: "/static/media/cd.020e3d1e.svg" - }, - { - revision: "497d91d1318438d0b128595d371d351e", - url: "/static/media/cg.497d91d1.svg" - }, - { - revision: "d51618940f7f8df58cae92f4acb930fd", - url: "/static/media/ch.d5161894.svg" - }, - { - revision: "869edc7171502fe62f15951ab9ab51ea", - url: "/static/media/ck.869edc71.svg" - }, - { - revision: "9d5227cbd7309dd8801518e8f3d9a7de", - url: "/static/media/cl.9d5227cb.svg" - }, - { - revision: "1334b221487d5b445f6802ddd024a5c8", - url: "/static/media/ci.1334b221.svg" - }, - { - revision: "c2814ac0b9d72075571409689b113eb9", - url: "/static/media/cn.c2814ac0.svg" - }, - { - revision: "433d22ad5949ca9376e7fee2398cf908", - url: "/static/media/co.433d22ad.svg" - }, - { - revision: "17f2e2c9827f7a093f9b94921ed16638", - url: "/static/media/cm.17f2e2c9.svg" - }, - { - revision: "20a9e6bf3919e282db3bf53d0e7820e2", - url: "/static/media/cr.20a9e6bf.svg" - }, - { - revision: "050058cb15f5c99a8710f96da1461357", - url: "/static/media/cu.050058cb.svg" - }, - { - revision: "07a0d3f998ff4c6e2213fb5a4863325b", - url: "/static/media/cw.07a0d3f9.svg" - }, - { - revision: "6b6994926cbbd25d691d0d029999492a", - url: "/static/media/cv.6b699492.svg" - }, - { - revision: "5180dbe51ead0d4850f3cc2db94d4cf9", - url: "/static/media/cx.5180dbe5.svg" - }, - { - revision: "657ef7aa34eb389fbf5217f52522e07f", - url: "/static/media/cy.657ef7aa.svg" - }, - { - revision: "6731f87258db1b3310d7c8b86efa97e6", - url: "/static/media/cz.6731f872.svg" - }, - { - revision: "01e89f77d4cd293951a32164b25ced60", - url: "/static/media/de.01e89f77.svg" - }, - { - revision: "44761537674d28555c1985159c2b2e88", - url: "/static/media/dk.44761537.svg" - }, - { - revision: "f4c086cc100130afc0642dc7bf1357de", - url: "/static/media/dj.f4c086cc.svg" - }, - { - revision: "89c91dc6769c54e9971dcda38bc747ef", - url: "/static/media/dm.89c91dc6.svg" - }, - { - revision: "57f366b0b55cf2fa11e3154e0865daf9", - url: "/static/media/ee.57f366b0.svg" - }, - { - revision: "333db1efebf7b037389acd563050c357", - url: "/static/media/dz.333db1ef.svg" - }, - { - revision: "918e57b82b2daae8608426807dc39b67", - url: "/static/media/ec.918e57b8.svg" - }, - { - revision: "07f2e96d05bbcbc1fa02e8d0678c598e", - url: "/static/media/eg.07f2e96d.svg" - }, - { - revision: "70738db67657a95db078a19581859e69", - url: "/static/media/er.70738db6.svg" - }, - { - revision: "e4f13505f93239f55cf1bf1615297172", - url: "/static/media/eh.e4f13505.svg" - }, - { - revision: "31aa0fc0721b95431f4b0dda48307c50", - url: "/static/media/et.31aa0fc0.svg" - }, - { - revision: "17beaf811c76ebaa6bcfdb8b5e8a7325", - url: "/static/media/eu.17beaf81.svg" - }, - { - revision: "2bd7d4dffe1fd474663f05041e95e46a", - url: "/static/media/fm.2bd7d4df.svg" - }, - { - revision: "58bcc4aff2131cf9d6eee5e30ec6fd62", - url: "/static/media/fi.58bcc4af.svg" - }, - { - revision: "d8ab6db91dc2db4b36c7b91bb1b4ebf6", - url: "/static/media/do.d8ab6db9.svg" - }, - { - revision: "b1ddba6040fc69b7d37591ffb7012787", - url: "/static/media/fj.b1ddba60.svg" - }, - { - revision: "dc9ed815f9a4bc59036c5fb3ef3aecca", - url: "/static/media/fo.dc9ed815.svg" - }, - { - revision: "5c64395d99f225e9c106c55c4c06ee69", - url: "/static/media/fk.5c64395d.svg" - }, - { - revision: "c6ca5440228101c2b83b4eb312a94731", - url: "/static/media/es.c6ca5440.svg" - }, - { - revision: "a178bcfbbbc26cb995fa19241b7a12a2", - url: "/static/media/fr.a178bcfb.svg" - }, - { - revision: "33442fb979e8f4f40b093bb4d6a39a7e", - url: "/static/media/ga.33442fb9.svg" - }, - { - revision: "943d406aa047964df31a94fc5a5021bc", - url: "/static/media/gb-nir.943d406a.svg" - }, - { - revision: "772350bf81e7b44332b5585cd15dfa3c", - url: "/static/media/gb-sct.772350bf.svg" - }, - { - revision: "a933214c8977a7009219775519a584b4", - url: "/static/media/gb-eng.a933214c.svg" - }, - { - revision: "4a878d5b85f694202ec0ccd16510be9c", - url: "/static/media/feather-webfont.4a878d5b.svg" - }, - { - revision: "b8e9cbc7ac23b572497cd2115bcf71c6", - url: "/static/media/feather-webfont.b8e9cbc7.ttf" - }, - { - revision: "2cf523cd335b115a5678b068b56c3011", - url: "/static/media/feather-webfont.2cf523cd.woff" - }, - { - revision: "cc5143b2b877ad1f2a9d7ddde2e55dee", - url: "/static/media/feather-webfont.cc5143b2.eot" - }, - { - revision: "a99e6d1ce661b5ec0118fa5e211dbdb1", - url: "/static/media/worldpay-dark.a99e6d1c.svg" - }, - { - revision: "d63620a3337795f043b232846be946f8", - url: "/static/media/worldpay.d63620a3.svg" - }, - { - revision: "4082e1b1ac8f311463c064a0875a8e5a", - url: "/static/media/westernunion.4082e1b1.svg" - }, - { - revision: "5f3974a30d3ead800491befb7af540a8", - url: "/static/media/westernunion-dark.5f3974a3.svg" - }, - { - revision: "c77724f331e1053188a5aa0d796ffc3b", - url: "/static/media/webmoney.c77724f3.svg" - }, - { - revision: "5c559c4c11d8fda02a9f9e86e1615b41", - url: "/static/media/webmoney-dark.5c559c4c.svg" - }, - { - revision: "a09152e75acbfee13fe82e13c54a77ad", - url: "/static/media/visa.a09152e7.svg" - }, - { - revision: "f6a55e1d4fc96499269717a964bc3984", - url: "/static/media/visa-dark.f6a55e1d.svg" - }, - { - revision: "012caff4df8cce6f2ea751366a4d0804", - url: "/static/media/verifone.012caff4.svg" - }, - { - revision: "1f0c2c56a34c8dce6fdbeaa80579e2c4", - url: "/static/media/verisign-dark.1f0c2c56.svg" - }, - { - revision: "e7b2a0bc53907540e752d6cfd9e95930", - url: "/static/media/verifone-dark.e7b2a0bc.svg" - }, - { - revision: "285de38e64669e7d6fdb6b88092a7adb", - url: "/static/media/unionpay.285de38e.svg" - }, - { - revision: "89b7d2ae90e9df97aa9e3a9940bac2c1", - url: "/static/media/ukash-dark.89b7d2ae.svg" - }, - { - revision: "c1a0e47dde0e275f4284a1e5b07a9219", - url: "/static/media/switch.c1a0e47d.svg" - }, - { - revision: "3684cf8229ff28f3054fa1d2a6095077", - url: "/static/media/verisign.3684cf82.svg" - }, - { - revision: "54599ad9cc5b0c3afea5db6b3d996e32", - url: "/static/media/switch-dark.54599ad9.svg" - }, - { - revision: "7a542b9ee5e6c96713e790bbd3854c85", - url: "/static/media/ukash.7a542b9e.svg" - }, - { - revision: "22beb1a2dc02dd5b8ecd72b776937af0", - url: "/static/media/unionpay-dark.22beb1a2.svg" - }, - { - revision: "025afc3556434d9a218b3de9ae6aab11", - url: "/static/media/stripe-dark.025afc35.svg" - }, - { - revision: "77c6af283968828069b3720792640fa9", - url: "/static/media/stripe.77c6af28.svg" - }, - { - revision: "48f113984b06dd75617b37d6d764a02b", - url: "/static/media/square.48f11398.svg" - }, - { - revision: "f7fcc525735b4166573bc49f7c418161", - url: "/static/media/solo.f7fcc525.svg" - }, - { - revision: "17da28b916977064d74363481913b58b", - url: "/static/media/solo-dark.17da28b9.svg" - }, - { - revision: "4db9c83cfd89dfc89536c33d2065ae16", - url: "/static/media/square-dark.4db9c83c.svg" - }, - { - revision: "b0d31271e85a4ee845ff91eeb2dc1ab4", - url: "/static/media/skrill.b0d31271.svg" - }, - { - revision: "a1a4a38c94505ac4c80974b84591059e", - url: "/static/media/skrill-dark.a1a4a38c.svg" - }, - { - revision: "2a87d23fcf628021ed81203dc2305938", - url: "/static/media/shopify.2a87d23f.svg" - }, - { - revision: "937412fda731ef86a0a3658eb6b1044f", - url: "/static/media/shopify-dark.937412fd.svg" - }, - { - revision: "3834e619996af0dec773a242f6fbf77c", - url: "/static/media/sepa-dark.3834e619.svg" - }, - { - revision: "c962e60b37391f1d7dd0a0ffacad256b", - url: "/static/media/sage.c962e60b.svg" - }, - { - revision: "1560c69d3cf081291eb13f477dc9e043", - url: "/static/media/sage-dark.1560c69d.svg" - }, - { - revision: "45d27bde30e9dcbf03da95a54dbe5720", - url: "/static/media/sepa.45d27bde.svg" - }, - { - revision: "44f32f32a552d578ccb68df55740c84b", - url: "/static/media/ripple.44f32f32.svg" - }, - { - revision: "a741b2b1463ca0e5cc9fd430004319b2", - url: "/static/media/ripple-dark.a741b2b1.svg" - }, - { - revision: "057164517322929b8b277ef36a63da87", - url: "/static/media/payza.05716451.svg" - }, - { - revision: "ece9e63914c3f788968b357cf6189e95", - url: "/static/media/payu.ece9e639.svg" - }, - { - revision: "aaf8d63fe0f20e267e21c89f0824edbf", - url: "/static/media/payza-dark.aaf8d63f.svg" - }, - { - revision: "2a3832c3bea2d4ad9b01ea999cbea582", - url: "/static/media/paysafecard-dark.2a3832c3.svg" - }, - { - revision: "80265cc7c79041d66e9437374b08894c", - url: "/static/media/payu-dark.80265cc7.svg" - }, - { - revision: "aa9749d2dbfa5fce884c050157002e4f", - url: "/static/media/paypal.aa9749d2.svg" - }, - { - revision: "0db2bc557a5ea15b0ba7f83b463776d3", - url: "/static/media/paysafecard.0db2bc55.svg" - }, - { - revision: "2abbaed44b22cd9ad7e423e88e9640f7", - url: "/static/media/paypal-dark.2abbaed4.svg" - }, - { - revision: "e460ab6b6da17bf959f8d123cfeb4e2e", - url: "/static/media/payoneer.e460ab6b.svg" - }, - { - revision: "8d95de50838be9eb99e9db6eb23a3610", - url: "/static/media/payoneer-dark.8d95de50.svg" - }, - { - revision: "992480f1d3c42a07ddcc81ef819277d7", - url: "/static/media/payone-dark.992480f1.svg" - }, - { - revision: "d8737b880a495605fed0d53b1a17100c", - url: "/static/media/paymill-dark.d8737b88.svg" - }, - { - revision: "6f9066168c1fdf21bb40228737af2d9b", - url: "/static/media/paymill.6f906616.svg" - }, - { - revision: "2c68e11e3f322e662dc62c4700d2e835", - url: "/static/media/payone.2c68e11e.svg" - }, - { - revision: "46f8af3b7129313668e112509e361f0d", - url: "/static/media/paybox.46f8af3b.svg" - }, - { - revision: "321bd555c37290b6a89acc1922a3e3ad", - url: "/static/media/paybox-dark.321bd555.svg" - }, - { - revision: "72f763a2ab7a69dcd6f92a1f448ff251", - url: "/static/media/okpay.72f763a2.svg" - }, - { - revision: "26eabf7a3b75ddbb402d926bb9510afa", - url: "/static/media/okpay-dark.26eabf7a.svg" - }, - { - revision: "8832c251bab55b7228f17ad1dcd93bcd", - url: "/static/media/ogone.8832c251.svg" - }, - { - revision: "5fa709fb52bd0947dc6ddd33eab567fc", - url: "/static/media/ogone-dark.5fa709fb.svg" - }, - { - revision: "798e0b4b9b2b5b2a6966e3160c8652d1", - url: "/static/media/neteller.798e0b4b.svg" - }, - { - revision: "63736caca924eb35fb9104d4f432cfb0", - url: "/static/media/neteller-dark.63736cac.svg" - }, - { - revision: "7df16d088d2d3fafc742fc011ab39191", - url: "/static/media/monero.7df16d08.svg" - }, - { - revision: "29d40dee70c67525aa54c6d462843f4a", - url: "/static/media/monero-dark.29d40dee.svg" - }, - { - revision: "b1695f2bf43376465adea7252ec7837f", - url: "/static/media/mastercard-dark.b1695f2b.svg" - }, - { - revision: "a6684d9315e2ded55b8ee33df8c370d5", - url: "/static/media/mastercard.a6684d93.svg" - }, - { - revision: "4642dfb3bacbec31479381e4800275b2", - url: "/static/media/laser.4642dfb3.svg" - }, - { - revision: "31a202b40107161647c50fac56384c29", - url: "/static/media/maestro.31a202b4.svg" - }, - { - revision: "0d91ff8fa73e4822b3df8578f6f90708", - url: "/static/media/maestro-dark.0d91ff8f.svg" - }, - { - revision: "758bd7b66e03b7b4f0feb8195ac30124", - url: "/static/media/laser-dark.758bd7b6.svg" - }, - { - revision: "c05b3bbaa7150d0b60d6dfa8c602f70f", - url: "/static/media/klarna.c05b3bba.svg" - }, - { - revision: "3a666a1e1aeba0c533c35132129e65db", - url: "/static/media/klarna-dark.3a666a1e.svg" - }, - { - revision: "2646bc518e3540d4639365448d02b23d", - url: "/static/media/jcb.2646bc51.svg" - }, - { - revision: "20a24d68389a7dfe17336496dc3e51b3", - url: "/static/media/ingenico.20a24d68.svg" - }, - { - revision: "5bef38951708ad075ebcd89dbed8d8d9", - url: "/static/media/ingenico-dark.5bef3895.svg" - }, - { - revision: "7f0e39ad58186b6fdbe5878970192668", - url: "/static/media/googlewallet.7f0e39ad.svg" - }, - { - revision: "f9bf701dcacbc6a9e40cc626153d6ff9", - url: "/static/media/jcb-dark.f9bf701d.svg" - }, - { - revision: "7337d9d063907f6fd8d49214982e18a6", - url: "/static/media/giropay.7337d9d0.svg" - }, - { - revision: "7cbe03bef872c536d6dbaa1f274ae0dc", - url: "/static/media/googlewallet-dark.7cbe03be.svg" - }, - { - revision: "54d6e672e8609e0b77d49f18c06430c7", - url: "/static/media/eway.54d6e672.svg" - }, - { - revision: "ff3c753ae34a95d2b30a9089319f29aa", - url: "/static/media/giropay-dark.ff3c753a.svg" - }, - { - revision: "bbf15466f81b7a24e9cc9e9522a2a709", - url: "/static/media/eway-dark.bbf15466.svg" - }, - { - revision: "862b611ad759b765022ea1cac513bbfa", - url: "/static/media/ebay.862b611a.svg" - }, - { - revision: "36f577700982f8fb3542d92a6c362650", - url: "/static/media/dwolla.36f57770.svg" - }, - { - revision: "ccae276756a625bc248c34c7c49ddcf4", - url: "/static/media/dwolla-dark.ccae2767.svg" - }, - { - revision: "2f4fe159d3189ca05916f3ad46cb1a6c", - url: "/static/media/discover.2f4fe159.svg" - }, - { - revision: "00f5c21f4be89a46de82c69e6259781c", - url: "/static/media/discover-dark.00f5c21f.svg" - }, - { - revision: "37695b626fb35b01215987cd7865ca7b", - url: "/static/media/directdebit.37695b62.svg" - }, - { - revision: "bd7ccde1aba2b3502e43a20b0dad4152", - url: "/static/media/ebay-dark.bd7ccde1.svg" - }, - { - revision: "bf510996f9f817b97d4618a413373998", - url: "/static/media/directdebit-dark.bf510996.svg" - }, - { - revision: "45249b1dd66c3b8425f9ce67f014d9ee", - url: "/static/media/dinersclub.45249b1d.svg" - }, - { - revision: "baff56e3fdcd57bc731c02c4878e7441", - url: "/static/media/dinersclub-dark.baff56e3.svg" - }, - { - revision: "b60982772ca2538902574c9790def63b", - url: "/static/media/coinkite.b6098277.svg" - }, - { - revision: "f50deb17e6e13ff02fe1f4c149d3166c", - url: "/static/media/coinkite-dark.f50deb17.svg" - }, - { - revision: "e7aa6cb086876090995d", - url: "/static/js/main.e7aa6cb0.chunk.js" - }, - { - revision: "f7d38984e9cfaa1bf3f98a0046862667", - url: "/static/media/clickandbuy-dark.f7d38984.svg" - }, - { - revision: "243a362ebddb29c473ace764e5b11e6b", - url: "/static/media/cirrus-dark.243a362e.svg" - }, - { - revision: "ffb94e65905ea7a299e8ee52944abef1", - url: "/static/media/bitpay.ffb94e65.svg" - }, - { - revision: "f86a15dac57d28c89e0b69ac3eee63f8", - url: "/static/media/bitpay-dark.f86a15da.svg" - }, - { - revision: "d9ac7b6156a3498ad0fd300b98f2f605", - url: "/static/media/bitcoin.d9ac7b61.svg" - }, - { - revision: "edaf60e16ce0cc50bf2d0b7a499036e4", - url: "/static/media/bitcoin-dark.edaf60e1.svg" - }, - { - revision: "983db5f2256f8e24e520ef7d1146ed3f", - url: "/static/media/cirrus.983db5f2.svg" - }, - { - revision: "8c0a0fa2bc07c9102ff49218b0ca9145", - url: "/static/media/bancontact.8c0a0fa2.svg" - }, - { - revision: "6e78609075a295f1627cd785a2005837", - url: "/static/media/bancontact-dark.6e786090.svg" - }, - { - revision: "e044dbdb76e1805843ae429c3c16bdd9", - url: "/static/media/applepay-dark.e044dbdb.svg" - }, - { - revision: "1ff3d3f0d176196bbd3aaf4a6ecf7dac", - url: "/static/media/applepay.1ff3d3f0.svg" - }, - { - revision: "b89abdaf46ce1b76d1f382de92ed7c0e", - url: "/static/media/americanexpress.b89abdaf.svg" - }, - { - revision: "5c500045ab6cd762cd5f9abd393c2577", - url: "/static/media/amazon.5c500045.svg" - }, - { - revision: "b178a57fcddb6156a5ec639d1b5d5a24", - url: "/static/media/amazon-dark.b178a57f.svg" - }, - { - revision: "c2ea2d77ce452a928487e9d62737ad4c", - url: "/static/media/americanexpress-dark.c2ea2d77.svg" - }, - { - revision: "b6a651d2cd0063d0e83b505c40f24dd7", - url: "/static/media/alipay-dark.b6a651d2.svg" - }, - { - revision: "31580e28ff89814332255e3f3ad510d6", - url: "/static/media/alipay.31580e28.svg" - }, - { - revision: "625866342c77dcf827cdc22d004c6227", - url: "/static/media/zm.62586634.svg" - }, - { - revision: "a2dc66505c31b7096ba48bac4557855c", - url: "/static/media/yt.a2dc6650.svg" - }, - { - revision: "d8ffed672eb363336a1ad1ad4dc965be", - url: "/static/media/za.d8ffed67.svg" - }, - { - revision: "55897575e3e0001ebfb8dcfba390495d", - url: "/static/media/ye.55897575.svg" - }, - { - revision: "23b64335ac552f3d33e7544da45a2508", - url: "/static/media/ws.23b64335.svg" - }, - { - revision: "65d58d809466b33a779ff1b029046730", - url: "/static/media/2checkout-dark.65d58d80.svg" - }, - { - revision: "4b4f5462b60b559d729a55f8719cf005", - url: "/static/media/wf.4b4f5462.svg" - }, - { - revision: "e14c0f5e3d367693fa699906a02119c6", - url: "/static/media/2checkout.e14c0f5e.svg" - }, - { - revision: "9a6c3abc25acb7444923135ab30b7cb9", - url: "/static/media/vu.9a6c3abc.svg" - }, - { - revision: "0b7571b87f2faaa3d8e3b5662636d574", - url: "/static/media/vn.0b7571b8.svg" - }, - { - revision: "e223cee52ee80138dfc25a1885c83186", - url: "/static/media/zw.e223cee5.svg" - }, - { - revision: "b3c0a20f217b35d1cf1111736130dac8", - url: "/static/media/vi.b3c0a20f.svg" - }, - { - revision: "3b3121b285747fdd0ca17486e084c675", - url: "/static/media/vg.3b3121b2.svg" - }, - { - revision: "6f48a1b9488fe66e13887fb43304c009", - url: "/static/media/ve.6f48a1b9.svg" - }, - { - revision: "f3912357d0a5339a1f402efefc89a8e7", - url: "/static/media/vc.f3912357.svg" - }, - { - revision: "791dfbdae7960b7482e949dfac7c829a", - url: "/static/media/uz.791dfbda.svg" - }, - { - revision: "a7e91b404efc4ad91c1360efd8e9cb4a", - url: "/static/media/uy.a7e91b40.svg" - }, - { - revision: "6b139c75ff4f94335205a2d93dc7e090", - url: "/static/media/va.6b139c75.svg" - }, - { - revision: "2382ea7ec7cc55bfe1cc7a3ea8326989", - url: "/static/media/us.2382ea7e.svg" - }, - { - revision: "1519b6c631d063c9e495cd9f3dfd0f66", - url: "/static/media/un.1519b6c6.svg" - }, - { - revision: "d5c9c20a3cfbf0c135ea7d58d29684f5", - url: "/static/media/tz.d5c9c20a.svg" - }, - { - revision: "a1fa2de39f9fdbd1e48a965bf697d700", - url: "/static/media/um.a1fa2de3.svg" - }, - { - revision: "1e070275fe2eb891e7a1b90ac3c3ee13", - url: "/static/media/ug.1e070275.svg" - }, - { - revision: "acc88be0743859f3c1d499c3117cfdcd", - url: "/static/media/ua.acc88be0.svg" - }, - { - revision: "7baefd1c21ecb97a0a48a0d738bf79dc", - url: "/static/media/tw.7baefd1c.svg" - }, - { - revision: "1a077ad0ee7788a6a1688dbfc5c12526", - url: "/static/media/tv.1a077ad0.svg" - }, - { - revision: "f09daa6dc55999ef79edf7d708ad1f90", - url: "/static/media/tt.f09daa6d.svg" - }, - { - revision: "aabe02c21bdc96b4499f10c7ead37008", - url: "/static/media/tr.aabe02c2.svg" - }, - { - revision: "fa884203b4e844943f89c290c02ea246", - url: "/static/media/to.fa884203.svg" - }, - { - revision: "ef273685b23f3978caf97e7fb0b2ea9d", - url: "/static/media/tn.ef273685.svg" - }, - { - revision: "f563fdae9a3ca98f28a3c4c03a6d766f", - url: "/static/media/tl.f563fdae.svg" - }, - { - revision: "22d4831b30e7a7ffa78d23628db3bdab", - url: "/static/media/tk.22d4831b.svg" - }, - { - revision: "502695871e6c9632d23ed1db99f4e102", - url: "/static/media/th.50269587.svg" - }, - { - revision: "d2132088d8448cd731e7047c1e432bf2", - url: "/static/media/tm.d2132088.svg" - }, - { - revision: "b6533ad31f2b20a30bba38b0f2de1d9b", - url: "/static/media/tj.b6533ad3.svg" - }, - { - revision: "b96ee5428e8c67d6b1fc8bf73925af34", - url: "/static/media/tg.b96ee542.svg" - }, - { - revision: "adc24fb28bb1688520b8ee3272929644", - url: "/static/media/tf.adc24fb2.svg" - }, - { - revision: "2f7d308e80bd8a87fa1d2c63aa74fc5a", - url: "/static/media/tc.2f7d308e.svg" - }, - { - revision: "079a252552085195fa1e74c55965d960", - url: "/static/media/td.079a2525.svg" - }, - { - revision: "1ae99e458e6568a1297a512ae21b85ba", - url: "/static/media/sz.1ae99e45.svg" - }, - { - revision: "0fedea0746db6aa80b93dc14293c1754", - url: "/static/media/sy.0fedea07.svg" - }, - { - revision: "d23d18072122ea995d7f4f4bea2300fe", - url: "/static/media/sx.d23d1807.svg" - }, - { - revision: "230410b519c6205157002ce21ff8d629", - url: "/static/media/st.230410b5.svg" - }, - { - revision: "0c7c9ffcd96a318fe1ed195441a6c2a9", - url: "/static/media/ss.0c7c9ffc.svg" - }, - { - revision: "3bdb1de25c626c766b62e2c1cca11ea9", - url: "/static/media/so.3bdb1de2.svg" - }, - { - revision: "65cdb1de480732b66f6a3675f49f2596", - url: "/static/media/sr.65cdb1de.svg" - }, - { - revision: "4dc603d122f3ede3b07bfb751ee3a59c", - url: "/static/media/sn.4dc603d1.svg" - }, - { - revision: "835d44f65482fc4d92251cb9eba71fa2", - url: "/static/media/sl.835d44f6.svg" - }, - { - revision: "f3eb4474892199b59c8ca7272069e6ba", - url: "/static/media/sm.f3eb4474.svg" - }, - { - revision: "a21150d5864835c762dd3bdb21e61320", - url: "/static/media/sv.a21150d5.svg" - }, - { - revision: "f44daf851804e866328d76cdd0b99074", - url: "/static/media/sk.f44daf85.svg" - }, - { - revision: "8331157c241082c3ad0f499b47737ac2", - url: "/static/media/sj.8331157c.svg" - }, - { - revision: "22b0739e53b62deb793917e7ba4c1892", - url: "/static/media/sg.22b0739e.svg" - }, - { - revision: "72f83c2946a14767d764c53baca31a7b", - url: "/static/media/si.72f83c29.svg" - }, - { - revision: "0726abdb26a803057f8e22205c03f172", - url: "/static/media/sh.0726abdb.svg" - }, - { - revision: "22475f5224df5500aa75813ba7608a23", - url: "/static/media/se.22475f52.svg" - }, - { - revision: "a14badd55e756d1248fb262f896a6a84", - url: "/static/media/sd.a14badd5.svg" - }, - { - revision: "67b058aefae79a7a8273c3a3ece09dae", - url: "/static/media/sa.67b058ae.svg" - }, - { - revision: "fdc11a48b5b254f92ffc220dc1935963", - url: "/static/media/sc.fdc11a48.svg" - }, - { - revision: "115ce3e59fc48f4e9307e69329ed0a85", - url: "/static/media/sb.115ce3e5.svg" - }, - { - revision: "46fb809f4912001f48fdc2b878e80f17", - url: "/static/media/rw.46fb809f.svg" - }, - { - revision: "517e32a1f8c51260abfd28e65123eac8", - url: "/static/media/ru.517e32a1.svg" - }, - { - revision: "552b5d9744e1cb43fe34d598cc391113", - url: "/static/media/ro.552b5d97.svg" - }, - { - revision: "a2dc66505c31b7096ba48bac4557855c", - url: "/static/media/re.a2dc6650.svg" - }, - { - revision: "426b1d470b7392ef3ea723342a138c6f", - url: "/static/media/rs.426b1d47.svg" - }, - { - revision: "20a4d7413504b137c05f202bbf385e9b", - url: "/static/media/qa.20a4d741.svg" - }, - { - revision: "0557592eea5bfc7ac4a3e3d41bde1e1c", - url: "/static/media/pw.0557592e.svg" - }, - { - revision: "abc5b39643482e82cb856bf160fa50fe", - url: "/static/media/py.abc5b396.svg" - }, - { - revision: "225ede3505309835812a31d8cd526332", - url: "/static/media/ps.225ede35.svg" - }, - { - revision: "a2dc66505c31b7096ba48bac4557855c", - url: "/static/media/pm.a2dc6650.svg" - }, - { - revision: "e489537c791c5a57f554f17b21b02868", - url: "/static/media/pr.e489537c.svg" - }, - { - revision: "bf813bfe31876e1a07e61f7ecdafd5a6", - url: "/static/media/pn.bf813bfe.svg" - }, - { - revision: "2257cff690948088abf92a799e89544e", - url: "/static/media/pl.2257cff6.svg" - }, - { - revision: "e129260bc90ab03c1f3b9f5452e0d66c", - url: "/static/media/pt.e129260b.svg" - }, - { - revision: "db891066a9bf98fd99cfa111abe7d535", - url: "/static/media/pk.db891066.svg" - }, - { - revision: "e444f903a3056c776d7eb977380fa0c6", - url: "/static/media/pg.e444f903.svg" - }, - { - revision: "8b5fbe69f9da3819f4887f6a01b8648e", - url: "/static/media/ph.8b5fbe69.svg" - }, - { - revision: "28a15c37093a6700fb9db6c92bb9f714", - url: "/static/media/pf.28a15c37.svg" - }, - { - revision: "4cabbfc6b407981692d9a034c04e3395", - url: "/static/media/pe.4cabbfc6.svg" - }, - { - revision: "910761356d647746a34206d23e138727", - url: "/static/media/pa.91076135.svg" - }, - { - revision: "9b7a06b9a821841e7a5fd0f3e3ab8cc4", - url: "/static/media/om.9b7a06b9.svg" - }, - { - revision: "03d7410ae73601f5ec7122019a2ab888", - url: "/static/media/nz.03d7410a.svg" - }, - { - revision: "e6bfaa15b7678d8441d4106e06376792", - url: "/static/media/nu.e6bfaa15.svg" - }, - { - revision: "f2afa5b9c3bb5ff4eac025d6a9e3e5ff", - url: "/static/media/nr.f2afa5b9.svg" - }, - { - revision: "8331157c241082c3ad0f499b47737ac2", - url: "/static/media/no.8331157c.svg" - }, - { - revision: "e6de69465e5e1ec155356a0827683a8a", - url: "/static/media/np.e6de6946.svg" - }, - { - revision: "de2a39a27acc28aebde8173acc4bdf6d", - url: "/static/media/nl.de2a39a2.svg" - }, - { - revision: "2b983496dce81d0805a0d92443e8000c", - url: "/static/media/ni.2b983496.svg" - }, - { - revision: "2ddc320beac15d92ffece6345b604540", - url: "/static/media/ng.2ddc320b.svg" - }, - { - revision: "fc2d0f07ea618d781e800bd8cd49d92c", - url: "/static/media/nf.fc2d0f07.svg" - }, - { - revision: "bad21adca6cd1a7c0498752de207dcbd", - url: "/static/media/ne.bad21adc.svg" - }, - { - revision: "a2dc66505c31b7096ba48bac4557855c", - url: "/static/media/nc.a2dc6650.svg" - }, - { - revision: "f38aead1dd402abc43b2e0dddd08ae47", - url: "/static/media/na.f38aead1.svg" - }, - { - revision: "cd1e97af5e343e6d7db5c8f8bbb40cac", - url: "/static/media/mz.cd1e97af.svg" - }, - { - revision: "5b33db847ef48920cfec09f0c2926e90", - url: "/static/media/mw.5b33db84.svg" - }, - { - revision: "aae5bd9cefde01ece247f58bf89a825c", - url: "/static/media/my.aae5bd9c.svg" - }, - { - revision: "cffcad7981a89128ffef6ec871c5ef96", - url: "/static/media/mt.cffcad79.svg" - }, - { - revision: "e343afe8028575ea736d2677db4f7744", - url: "/static/media/mv.e343afe8.svg" - }, - { - revision: "974b9e6c380a062b6504150999965d5f", - url: "/static/media/mu.974b9e6c.svg" - }, - { - revision: "184d53d145cbbb2eafe2bc7a3bd66c62", - url: "/static/media/mx.184d53d1.svg" - }, - { - revision: "8b73c710b4a9a2c91ed2683bd2ba2a41", - url: "/static/media/ms.8b73c710.svg" - }, - { - revision: "36f1d6f2d8b53af76065ce17e6189104", - url: "/static/media/mo.36f1d6f2.svg" - }, - { - revision: "6b3d082dde2cd6355e7dd6194b258da7", - url: "/static/media/mr.6b3d082d.svg" - }, - { - revision: "4c4286cd431a0194e7d35bcc875537b7", - url: "/static/media/mq.4c4286cd.svg" - }, - { - revision: "fcdc8e3981178bdf4bf5f382fa7e7dab", - url: "/static/media/mp.fcdc8e39.svg" - }, - { - revision: "cfd48e450bb31f3dc56b78fdac465bc0", - url: "/static/media/mn.cfd48e45.svg" - }, - { - revision: "e6d7c5a4187b1fd8ab643d0e5d2f5bd1", - url: "/static/media/mm.e6d7c5a4.svg" - }, - { - revision: "29cb0cb257ce61901ab1d97c97200be9", - url: "/static/media/mk.29cb0cb2.svg" - }, - { - revision: "be076fd925ea2dd5a74f6a552166ba71", - url: "/static/media/ml.be076fd9.svg" - }, - { - revision: "0c0da5f0631b226d95fd57929b9e4b4b", - url: "/static/media/mg.0c0da5f0.svg" - }, - { - revision: "a3bb001b15d05e4a8974729fa75f9247", - url: "/static/media/mh.a3bb001b.svg" - }, - { - revision: "a178bcfbbbc26cb995fa19241b7a12a2", - url: "/static/media/mf.a178bcfb.svg" - }, - { - revision: "4241d3ff964cfdb68da07bb0f78520f4", - url: "/static/media/mc.4241d3ff.svg" - }, - { - revision: "399015d8b358e8c3c2c1a3e699752e63", - url: "/static/media/me.399015d8.svg" - }, - { - revision: "f9aceffb03e9764fac60e5aafe3743ec", - url: "/static/media/md.f9aceffb.svg" - }, - { - revision: "8c27c49311f54ab8d011b8eacf6c63cb", - url: "/static/media/ma.8c27c493.svg" - }, - { - revision: "83353fa9cde68c8e128f85724e743e75", - url: "/static/media/lv.83353fa9.svg" - }, - { - revision: "06956a1377123bf7bf98076217a07361", - url: "/static/media/lu.06956a13.svg" - }, - { - revision: "14b63eab7de31bd29ffcdc4002433cd6", - url: "/static/media/lt.14b63eab.svg" - }, - { - revision: "ededce3248f5c7f3e52a48bcfa55ac01", - url: "/static/media/ly.ededce32.svg" - }, - { - revision: "700ddad000d732b2603dcde0195ea3e7", - url: "/static/media/ls.700ddad0.svg" - }, - { - revision: "f0a4f4f6d893038aa99ccbcb7f6e5271", - url: "/static/media/lk.f0a4f4f6.svg" - }, - { - revision: "5485e606cf2dcf18e30b88581f14a459", - url: "/static/media/lr.5485e606.svg" - }, - { - revision: "10e0d5b28508b7a92f02b01c8f54bfe7", - url: "/static/media/li.10e0d5b2.svg" - }, - { - revision: "6c2940dae95d15b98cf38bcf44816d21", - url: "/static/media/lc.6c2940da.svg" - }, - { - revision: "4981974031355cb8cb9fa6ae351ec6cf", - url: "/static/media/lb.49819740.svg" - }, - { - revision: "bdfc4ab5e964e3466fcf31b5ec4bf87b", - url: "/static/media/la.bdfc4ab5.svg" - }, - { - revision: "f7c3a515e4c61b03f6818233ded0bd8c", - url: "/static/media/ky.f7c3a515.svg" - }, - { - revision: "3e24a94a1aee5cfa3c34f2fa6f8f1845", - url: "/static/media/kw.3e24a94a.svg" - }, - { - revision: "529db212e9de897dc2dd42f4ad7f8fd3", - url: "/static/media/kz.529db212.svg" - }, - { - revision: "32f23fafe64cce64d0e30c1d80e761ae", - url: "/static/media/kr.32f23faf.svg" - }, - { - revision: "cd351374021fde2537ae578691612f30", - url: "/static/media/km.cd351374.svg" - }, - { - revision: "b2729dfae51752a2cb41de576c90b6bb", - url: "/static/media/kp.b2729dfa.svg" - }, - { - revision: "fbe824dcd1ef2519d2d21f189a345c2a", - url: "/static/media/ki.fbe824dc.svg" - }, - { - revision: "7ab9462c3019492674aa27c5f42df7f1", - url: "/static/media/kn.7ab9462c.svg" - }, - { - revision: "15b698f31b8bec3028bea1726cea84fb", - url: "/static/media/ke.15b698f3.svg" - }, - { - revision: "de33c0489053970bffc24559744aaae3", - url: "/static/media/kg.de33c048.svg" - }, - { - revision: "fd2646810e3b7a16d5ff0e16401fcf94", - url: "/static/media/jp.fd264681.svg" - }, - { - revision: "d14059401101d457efe14ba2495e69c6", - url: "/static/media/jo.d1405940.svg" - }, - { - revision: "6a9e1b932b348bea888a9cb0a21ad581", - url: "/static/media/je.6a9e1b93.svg" - }, - { - revision: "bd6b5ff3c79cb3d80d524f342ff99ba4", - url: "/static/media/it.bd6b5ff3.svg" - }, - { - revision: "bfffb443939dc4de9a1926380b3c99b4", - url: "/static/media/kh.bfffb443.svg" - }, - { - revision: "3cb275a7c517640ff251ce419ba5a7be", - url: "/static/media/ir.3cb275a7.svg" - }, - { - revision: "7db0ffd8c9e9717bf8a4e670b8e14de8", - url: "/static/media/jm.7db0ffd8.svg" - }, - { - revision: "ec1fb8765fe74b0912ab152afe850c38", - url: "/static/media/is.ec1fb876.svg" - }, - { - revision: "2d667fbb3870fa62aa27eece3a00196c", - url: "/static/media/in.2d667fbb.svg" - }, - { - revision: "61fca1841f4f8e1b031eeeb7a7708650", - url: "/static/media/iq.61fca184.svg" - }, - { - revision: "0ea7e9dad5f9fce9cdee314eea294da8", - url: "/static/media/il.0ea7e9da.svg" - }, - { - revision: "d609c4e7bbb267cc920b9bfacdf8c553", - url: "/static/media/ie.d609c4e7.svg" - }, - { - revision: "19884f0c27b6b1a57a12fdb7b682eed2", - url: "/static/media/im.19884f0c.svg" - }, - { - revision: "2e0c61df4402b9748b394cf508f1a0c7", - url: "/static/media/io.2e0c61df.svg" - }, - { - revision: "ee020a0f5bc9d6586b97f9a9dfea47a0", - url: "/static/media/id.ee020a0f.svg" - }, - { - revision: "d0404e4a48a02f0e5b393e7f88d02648", - url: "/static/media/ht.d0404e4a.svg" - }, - { - revision: "b43f38576524ed7038b5afd6337d5759", - url: "/static/media/hm.b43f3857.svg" - }, - { - revision: "fb606eb1063380a1c9d858161cf5f0a7", - url: "/static/media/hk.fb606eb1.svg" - }, - { - revision: "79e564a4cd82e29e24b5a3ff6c6d914e", - url: "/static/media/hr.79e564a4.svg" - }, - { - revision: "19bcfc3477c49626f2f9e4291e3f81bd", - url: "/static/media/gy.19bcfc34.svg" - }, - { - revision: "3d726baafa62f8f9fee22363226fb75c", - url: "/static/media/hn.3d726baa.svg" - }, - { - revision: "e1d47aa4658950ee3f11d125f19a604a", - url: "/static/media/gw.e1d47aa4.svg" - }, - { - revision: "ad34e604908c0fd1e96af29a7e9b983f", - url: "/static/media/gu.ad34e604.svg" - }, - { - revision: "3721691749ae1da7133203472974ea5f", - url: "/static/media/gs.37216917.svg" - }, - { - revision: "0b689ffe012a208dbd4609b8e7a6ce4c", - url: "/static/media/gt.0b689ffe.svg" - }, - { - revision: "9a9a62a1f4f53cc87d02925098293360", - url: "/static/media/gr.9a9a62a1.svg" - }, - { - revision: "a178bcfbbbc26cb995fa19241b7a12a2", - url: "/static/media/gp.a178bcfb.svg" - }, - { - revision: "6bbb0e7695e648aa9d7e25eff7165284", - url: "/static/media/gq.6bbb0e76.svg" - }, - { - revision: "e472dff761a5641c37c985858a735dc3", - url: "/static/media/gn.e472dff7.svg" - }, - { - revision: "9423800e095be53df9249808ce63306c", - url: "/static/media/gm.9423800e.svg" - }, - { - revision: "a8abaf3779c44dbb5d3604b621d899fc", - url: "/static/media/hu.a8abaf37.svg" - }, - { - revision: "c9543d40b95a35ff339fe78d6184b6d1", - url: "/static/media/gi.c9543d40.svg" - }, - { - revision: "d02c42ea2b63c1131bb36da347ac3490", - url: "/static/media/gl.d02c42ea.svg" - }, - { - revision: "d339aeb27fefd04b3c8238b7d8f26473", - url: "/static/media/gg.d339aeb2.svg" - }, - { - revision: "d4b35e14b2cdd6bb630a7b2c8902d7b7", - url: "/static/media/gh.d4b35e14.svg" - }, - { - revision: "4ea8e1590ad37f3d4fb8c58c7906a73c", - url: "/static/media/gf.4ea8e159.svg" - }, - { - revision: "c17d779e8552e59c9ef032f0a432fcfb", - url: "/static/media/gd.c17d779e.svg" - }, - { - revision: "334a8275142fd63934abf3a8f8c5a913", - url: "/static/media/ge.334a8275.svg" - }, - { - revision: "5638bbd9874edd22c39b0c4a54b1de21", - url: "/static/media/gb.5638bbd9.svg" - }, - { - revision: "3424674880e35d2c3d70", - url: "/static/css/1.13813cb9.chunk.css" - }, - { - revision: "5b301ae00b828bb39af8755bfb1feffc", - url: "/index.html" - } -]; diff --git a/onvm_web/web-build/service-worker.js b/onvm_web/web-build/service-worker.js index 3dcd4958d..865dbae94 100644 --- a/onvm_web/web-build/service-worker.js +++ b/onvm_web/web-build/service-worker.js @@ -1,33 +1 @@ -/** - * Welcome to your Workbox-powered service worker! - * - * You'll need to register this file in your web app and you should - * disable HTTP caching for this file too. - * See https://goo.gl/nhQhGp - * - * The rest of the code is auto-generated. Please don't update this file - * directly; instead, make changes to your Workbox build configuration - * and re-run your build process. - * See https://goo.gl/2aRDsh - */ - -importScripts( - "https://storage.googleapis.com/workbox-cdn/releases/3.6.2/workbox-sw.js" -); - -importScripts("/precache-manifest.bb594bddcd5aa10ab1d2cea76bb313fb.js"); - -workbox.clientsClaim(); - -/** - * The workboxSW.precacheAndRoute() method efficiently caches and responds to - * requests for URLs in the manifest. - * See https://goo.gl/S9QRab - */ -self.__precacheManifest = [].concat(self.__precacheManifest || []); -workbox.precaching.suppressWarnings(); -workbox.precaching.precacheAndRoute(self.__precacheManifest, {}); - -workbox.routing.registerNavigationRoute("/index.html", { - blacklist: [/^\/_/, /\/[^\/]+\.[^\/]+$/] -}); +importScripts("https://storage.googleapis.com/workbox-cdn/releases/3.6.2/workbox-sw.js");importScripts("/precache-manifest.a7124342f5fb4923d927c4421cb76d27.js");workbox.clientsClaim();self.__precacheManifest=[].concat(self.__precacheManifest||[]);workbox.precaching.suppressWarnings();workbox.precaching.precacheAndRoute(self.__precacheManifest,{});workbox.routing.registerNavigationRoute("/index.html",{blacklist:[/^\/_/,/\/[^\/]+\.[^\/]+$/]}); \ No newline at end of file diff --git a/onvm_web/web-build/static/css/1.13813cb9.chunk.css b/onvm_web/web-build/static/css/1.13813cb9.chunk.css deleted file mode 100644 index ffcaec023..000000000 --- a/onvm_web/web-build/static/css/1.13813cb9.chunk.css +++ /dev/null @@ -1,7 +0,0 @@ -/*! - * Bootstrap v4.0.0 (https://getbootstrap.com) - * Copyright 2011-2018 The Bootstrap Authors - * Copyright 2011-2018 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */:root{--blue:#467fcf;--indigo:#6574cd;--purple:#a55eea;--pink:#f66d9b;--red:#cd201f;--orange:#fd9644;--yellow:#f1c40f;--green:#5eba00;--teal:#2bcbba;--cyan:#17a2b8;--white:#fff;--gray:#868e96;--gray-dark:#343a40;--azure:#45aaf2;--lime:#7bd235;--primary:#467fcf;--secondary:#868e96;--success:#5eba00;--info:#45aaf2;--warning:#f1c40f;--danger:#cd201f;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1280px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";--font-family-monospace:Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,:after,:before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:transparent}@-ms-viewport{width:device-width}article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:Source Sans Pro,-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica Neue,Arial,sans-serif;font-size:.9375rem;font-weight:400;line-height:1.5;color:#495057;text-align:left;background-color:#f5f7fb}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:initial;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.66em}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0}address{font-style:normal;line-height:inherit}address,dl,ol,ul{margin-bottom:1rem}dl,ol,ul{margin-top:0}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}dfn{font-style:italic}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}a{color:#467fcf;text-decoration:none;background-color:initial;-webkit-text-decoration-skip:objects}a:hover{color:#295a9f;text-decoration:underline}a:not([href]):not([tabindex]),a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg:not(:root){overflow:hidden}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#9aa0ac;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:initial}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.66em;font-family:inherit;font-weight:600;line-height:1.1;color:inherit}.h1,h1{font-size:2rem}.h2,h2{font-size:1.75rem}.h3,h3{font-size:1.5rem}.h4,h4{font-size:1.125rem}.h5,h5{font-size:1rem}.h6,h6{font-size:.875rem}.lead{font-size:1.171875rem;font-weight:300}.display-1{font-size:4.5rem}.display-1,.display-2{font-weight:300;line-height:1.1}.display-2{font-size:4rem}.display-3{font-size:3.5rem}.display-3,.display-4{font-weight:300;line-height:1.1}.display-4{font-size:3rem}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,40,100,.12)}.small,small{font-size:87.5%;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-inline,.list-unstyled{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.171875rem}.blockquote-footer{display:block;font-size:80%;color:#868e96}.blockquote-footer:before{content:"\2014 \A0"}.img-fluid,.img-thumbnail{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:3px}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#868e96}code,kbd,pre,samp{font-family:Monaco,Consolas,Liberation Mono,Courier New,monospace}code{font-size:85%;word-break:break-word}a>code,code{color:inherit}kbd{padding:.2rem .4rem;font-size:85%;color:#fff;background-color:#343a40;border-radius:3px}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;color:#212529}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{width:100%;padding-right:.75rem;padding-left:.75rem;margin-right:auto;margin-left:auto}@media (min-width:576px){.container{max-width:540px}}@media (min-width:768px){.container{max-width:720px}}@media (min-width:992px){.container{max-width:960px}}@media (min-width:1280px){.container{max-width:1200px}}.container-fluid{width:100%;padding-right:.75rem;padding-left:.75rem;margin-right:auto;margin-left:auto}.row{display:flex;flex-wrap:wrap;margin-right:-.75rem;margin-left:-.75rem}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-10,.col-11,.col-12,.col-auto,.col-lg,.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-auto,.col-md,.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12,.col-md-auto,.col-sm,.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-auto{position:relative;width:100%;min-height:1px;padding-right:.75rem;padding-left:.75rem}.col{flex-basis:0;flex-grow:1;max-width:100%}.col-auto{flex:0 0 auto;width:auto;max-width:none}.col-1{flex:0 0 8.33333333%;max-width:8.33333333%}.col-2{flex:0 0 16.66666667%;max-width:16.66666667%}.col-3{flex:0 0 25%;max-width:25%}.col-4{flex:0 0 33.33333333%;max-width:33.33333333%}.col-5{flex:0 0 41.66666667%;max-width:41.66666667%}.col-6{flex:0 0 50%;max-width:50%}.col-7{flex:0 0 58.33333333%;max-width:58.33333333%}.col-8{flex:0 0 66.66666667%;max-width:66.66666667%}.col-9{flex:0 0 75%;max-width:75%}.col-10{flex:0 0 83.33333333%;max-width:83.33333333%}.col-11{flex:0 0 91.66666667%;max-width:91.66666667%}.col-12{flex:0 0 100%;max-width:100%}.order-first{order:-1}.order-last{order:13}.order-0{order:0}.order-1{order:1}.order-2{order:2}.order-3{order:3}.order-4{order:4}.order-5{order:5}.order-6{order:6}.order-7{order:7}.order-8{order:8}.order-9{order:9}.order-10{order:10}.order-11{order:11}.order-12{order:12}.offset-1{margin-left:8.33333333%}.offset-2{margin-left:16.66666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.33333333%}.offset-5{margin-left:41.66666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.33333333%}.offset-8{margin-left:66.66666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.33333333%}.offset-11{margin-left:91.66666667%}@media (min-width:576px){.col-sm{flex-basis:0;flex-grow:1;max-width:100%}.col-sm-auto{flex:0 0 auto;width:auto;max-width:none}.col-sm-1{flex:0 0 8.33333333%;max-width:8.33333333%}.col-sm-2{flex:0 0 16.66666667%;max-width:16.66666667%}.col-sm-3{flex:0 0 25%;max-width:25%}.col-sm-4{flex:0 0 33.33333333%;max-width:33.33333333%}.col-sm-5{flex:0 0 41.66666667%;max-width:41.66666667%}.col-sm-6{flex:0 0 50%;max-width:50%}.col-sm-7{flex:0 0 58.33333333%;max-width:58.33333333%}.col-sm-8{flex:0 0 66.66666667%;max-width:66.66666667%}.col-sm-9{flex:0 0 75%;max-width:75%}.col-sm-10{flex:0 0 83.33333333%;max-width:83.33333333%}.col-sm-11{flex:0 0 91.66666667%;max-width:91.66666667%}.col-sm-12{flex:0 0 100%;max-width:100%}.order-sm-first{order:-1}.order-sm-last{order:13}.order-sm-0{order:0}.order-sm-1{order:1}.order-sm-2{order:2}.order-sm-3{order:3}.order-sm-4{order:4}.order-sm-5{order:5}.order-sm-6{order:6}.order-sm-7{order:7}.order-sm-8{order:8}.order-sm-9{order:9}.order-sm-10{order:10}.order-sm-11{order:11}.order-sm-12{order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.33333333%}.offset-sm-2{margin-left:16.66666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.33333333%}.offset-sm-5{margin-left:41.66666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.33333333%}.offset-sm-8{margin-left:66.66666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.33333333%}.offset-sm-11{margin-left:91.66666667%}}@media (min-width:768px){.col-md{flex-basis:0;flex-grow:1;max-width:100%}.col-md-auto{flex:0 0 auto;width:auto;max-width:none}.col-md-1{flex:0 0 8.33333333%;max-width:8.33333333%}.col-md-2{flex:0 0 16.66666667%;max-width:16.66666667%}.col-md-3{flex:0 0 25%;max-width:25%}.col-md-4{flex:0 0 33.33333333%;max-width:33.33333333%}.col-md-5{flex:0 0 41.66666667%;max-width:41.66666667%}.col-md-6{flex:0 0 50%;max-width:50%}.col-md-7{flex:0 0 58.33333333%;max-width:58.33333333%}.col-md-8{flex:0 0 66.66666667%;max-width:66.66666667%}.col-md-9{flex:0 0 75%;max-width:75%}.col-md-10{flex:0 0 83.33333333%;max-width:83.33333333%}.col-md-11{flex:0 0 91.66666667%;max-width:91.66666667%}.col-md-12{flex:0 0 100%;max-width:100%}.order-md-first{order:-1}.order-md-last{order:13}.order-md-0{order:0}.order-md-1{order:1}.order-md-2{order:2}.order-md-3{order:3}.order-md-4{order:4}.order-md-5{order:5}.order-md-6{order:6}.order-md-7{order:7}.order-md-8{order:8}.order-md-9{order:9}.order-md-10{order:10}.order-md-11{order:11}.order-md-12{order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.33333333%}.offset-md-2{margin-left:16.66666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.33333333%}.offset-md-5{margin-left:41.66666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.33333333%}.offset-md-8{margin-left:66.66666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.33333333%}.offset-md-11{margin-left:91.66666667%}}@media (min-width:992px){.col-lg{flex-basis:0;flex-grow:1;max-width:100%}.col-lg-auto{flex:0 0 auto;width:auto;max-width:none}.col-lg-1{flex:0 0 8.33333333%;max-width:8.33333333%}.col-lg-2{flex:0 0 16.66666667%;max-width:16.66666667%}.col-lg-3{flex:0 0 25%;max-width:25%}.col-lg-4{flex:0 0 33.33333333%;max-width:33.33333333%}.col-lg-5{flex:0 0 41.66666667%;max-width:41.66666667%}.col-lg-6{flex:0 0 50%;max-width:50%}.col-lg-7{flex:0 0 58.33333333%;max-width:58.33333333%}.col-lg-8{flex:0 0 66.66666667%;max-width:66.66666667%}.col-lg-9{flex:0 0 75%;max-width:75%}.col-lg-10{flex:0 0 83.33333333%;max-width:83.33333333%}.col-lg-11{flex:0 0 91.66666667%;max-width:91.66666667%}.col-lg-12{flex:0 0 100%;max-width:100%}.order-lg-first{order:-1}.order-lg-last{order:13}.order-lg-0{order:0}.order-lg-1{order:1}.order-lg-2{order:2}.order-lg-3{order:3}.order-lg-4{order:4}.order-lg-5{order:5}.order-lg-6{order:6}.order-lg-7{order:7}.order-lg-8{order:8}.order-lg-9{order:9}.order-lg-10{order:10}.order-lg-11{order:11}.order-lg-12{order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.33333333%}.offset-lg-2{margin-left:16.66666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.33333333%}.offset-lg-5{margin-left:41.66666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.33333333%}.offset-lg-8{margin-left:66.66666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.33333333%}.offset-lg-11{margin-left:91.66666667%}}@media (min-width:1280px){.col-xl{flex-basis:0;flex-grow:1;max-width:100%}.col-xl-auto{flex:0 0 auto;width:auto;max-width:none}.col-xl-1{flex:0 0 8.33333333%;max-width:8.33333333%}.col-xl-2{flex:0 0 16.66666667%;max-width:16.66666667%}.col-xl-3{flex:0 0 25%;max-width:25%}.col-xl-4{flex:0 0 33.33333333%;max-width:33.33333333%}.col-xl-5{flex:0 0 41.66666667%;max-width:41.66666667%}.col-xl-6{flex:0 0 50%;max-width:50%}.col-xl-7{flex:0 0 58.33333333%;max-width:58.33333333%}.col-xl-8{flex:0 0 66.66666667%;max-width:66.66666667%}.col-xl-9{flex:0 0 75%;max-width:75%}.col-xl-10{flex:0 0 83.33333333%;max-width:83.33333333%}.col-xl-11{flex:0 0 91.66666667%;max-width:91.66666667%}.col-xl-12{flex:0 0 100%;max-width:100%}.order-xl-first{order:-1}.order-xl-last{order:13}.order-xl-0{order:0}.order-xl-1{order:1}.order-xl-2{order:2}.order-xl-3{order:3}.order-xl-4{order:4}.order-xl-5{order:5}.order-xl-6{order:6}.order-xl-7{order:7}.order-xl-8{order:8}.order-xl-9{order:9}.order-xl-10{order:10}.order-xl-11{order:11}.order-xl-12{order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.33333333%}.offset-xl-2{margin-left:16.66666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.33333333%}.offset-xl-5{margin-left:41.66666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.33333333%}.offset-xl-8{margin-left:66.66666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.33333333%}.offset-xl-11{margin-left:91.66666667%}}.table,.text-wrap table{width:100%;max-width:100%;margin-bottom:1rem;background-color:initial}.table td,.table th,.text-wrap table td,.text-wrap table th{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th,.text-wrap table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody,.text-wrap table tbody+tbody{border-top:2px solid #dee2e6}.table .table,.table .text-wrap table,.text-wrap .table table,.text-wrap table .table,.text-wrap table table{background-color:#f5f7fb}.table-sm td,.table-sm th{padding:.3rem}.table-bordered,.table-bordered td,.table-bordered th,.text-wrap table,.text-wrap table td,.text-wrap table th{border:1px solid #dee2e6}.table-bordered thead td,.table-bordered thead th,.text-wrap table thead td,.text-wrap table thead th{border-bottom-width:2px}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.02)}.table-hover tbody tr:hover{background-color:rgba(0,0,0,.04)}.table-primary,.table-primary>td,.table-primary>th{background-color:#cbdbf2}.table-hover .table-primary:hover,.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#b7cded}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#dddfe2}.table-hover .table-secondary:hover,.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#cfd2d6}.table-success,.table-success>td,.table-success>th{background-color:#d2ecb8}.table-hover .table-success:hover,.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#c5e7a4}.table-info,.table-info>td,.table-info>th{background-color:#cbe7fb}.table-hover .table-info:hover,.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#b3dcf9}.table-warning,.table-warning>td,.table-warning>th{background-color:#fbeebc}.table-hover .table-warning:hover,.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#fae8a4}.table-danger,.table-danger>td,.table-danger>th{background-color:#f1c1c0}.table-hover .table-danger:hover,.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#ecacab}.table-light,.table-light>td,.table-light>th{background-color:#fdfdfe}.table-hover .table-light:hover,.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>td,.table-dark>th{background-color:#c6c8ca}.table-hover .table-dark:hover,.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>td,.table-active>th,.table-hover .table-active:hover,.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,.04)}.table .thead-dark th,.text-wrap table .thead-dark th{color:#f5f7fb;background-color:#212529;border-color:#32383e}.table .thead-light th,.text-wrap table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#f5f7fb;background-color:#212529}.table-dark td,.table-dark th,.table-dark thead th{border-color:#32383e}.table-dark.table-bordered,.text-wrap table.table-dark{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:hsla(0,0%,100%,.05)}.table-dark.table-hover tbody tr:hover{background-color:hsla(0,0%,100%,.075)}@media (max-width:575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-sm>.table-bordered,.text-wrap .table-responsive-sm>table{border:0}}@media (max-width:767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-md>.table-bordered,.text-wrap .table-responsive-md>table{border:0}}@media (max-width:991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-lg>.table-bordered,.text-wrap .table-responsive-lg>table{border:0}}@media (max-width:1279.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-xl>.table-bordered,.text-wrap .table-responsive-xl>table{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive>.table-bordered,.text-wrap .table-responsive>table{border:0}.form-control{display:block;width:100%;padding:.375rem .75rem;font-size:.9375rem;line-height:1.6;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,40,100,.12);border-radius:3px;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}.form-control::-ms-expand{background-color:initial;border:0}.form-control:focus{color:#495057;background-color:#fff;border-color:#1991eb;outline:0;box-shadow:0 0 0 2px rgba(70,127,207,.25)}.form-control::-webkit-input-placeholder{color:#adb5bd;opacity:1}.form-control:-ms-input-placeholder{color:#adb5bd;opacity:1}.form-control::-ms-input-placeholder{color:#adb5bd;opacity:1}.form-control::placeholder{color:#adb5bd;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#f8f9fa;opacity:1}select.form-control:not([size]):not([multiple]){height:2.375rem}select.form-control:focus::-ms-value{color:#495057;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.6}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.125rem;line-height:1.44444444}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.14285714}.form-control-plaintext{display:block;width:100%;padding-top:.375rem;padding-bottom:.375rem;margin-bottom:0;line-height:1.6;background-color:initial;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm,.input-group-lg>.form-control-plaintext.form-control,.input-group-lg>.input-group-append>.form-control-plaintext.btn,.input-group-lg>.input-group-append>.form-control-plaintext.input-group-text,.input-group-lg>.input-group-prepend>.form-control-plaintext.btn,.input-group-lg>.input-group-prepend>.form-control-plaintext.input-group-text,.input-group-sm>.form-control-plaintext.form-control,.input-group-sm>.input-group-append>.form-control-plaintext.btn,.input-group-sm>.input-group-append>.form-control-plaintext.input-group-text,.input-group-sm>.input-group-prepend>.form-control-plaintext.btn,.input-group-sm>.input-group-prepend>.form-control-plaintext.input-group-text{padding-right:0;padding-left:0}.form-control-sm,.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{padding:.25rem .5rem;font-size:.875rem;line-height:1.14285714;border-radius:3px}.input-group-sm>.input-group-append>select.btn:not([size]):not([multiple]),.input-group-sm>.input-group-append>select.input-group-text:not([size]):not([multiple]),.input-group-sm>.input-group-prepend>select.btn:not([size]):not([multiple]),.input-group-sm>.input-group-prepend>select.input-group-text:not([size]):not([multiple]),.input-group-sm>select.form-control:not([size]):not([multiple]),select.form-control-sm:not([size]):not([multiple]){height:calc(1.8125rem + 2px)}.form-control-lg,.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{padding:.5rem 1rem;font-size:1.125rem;line-height:1.44444444;border-radius:3px}.input-group-lg>.input-group-append>select.btn:not([size]):not([multiple]),.input-group-lg>.input-group-append>select.input-group-text:not([size]):not([multiple]),.input-group-lg>.input-group-prepend>select.btn:not([size]):not([multiple]),.input-group-lg>.input-group-prepend>select.input-group-text:not([size]):not([multiple]),.input-group-lg>select.form-control:not([size]):not([multiple]),select.form-control-lg:not([size]):not([multiple]){height:calc(2.6875rem + 2px)}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:flex;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label{color:#9aa0ac}.form-check-label{margin-bottom:0}.form-check-inline{display:inline-flex;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:87.5%;color:#5eba00}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.5rem;margin-top:.1rem;font-size:.875rem;line-height:1;color:#fff;background-color:rgba(94,186,0,.8);border-radius:.2rem}.custom-select.is-valid,.form-control.is-valid,.was-validated .custom-select:valid,.was-validated .form-control:valid{border-color:#5eba00}.custom-select.is-valid:focus,.form-control.is-valid:focus,.was-validated .custom-select:valid:focus,.was-validated .form-control:valid:focus{border-color:#5eba00;box-shadow:0 0 0 2px rgba(94,186,0,.25)}.custom-select.is-valid~.valid-feedback,.custom-select.is-valid~.valid-tooltip,.form-control.is-valid~.valid-feedback,.form-control.is-valid~.valid-tooltip,.was-validated .custom-select:valid~.valid-feedback,.was-validated .custom-select:valid~.valid-tooltip,.was-validated .form-control:valid~.valid-feedback,.was-validated .form-control:valid~.valid-tooltip{display:block}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#5eba00}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#5eba00}.custom-control-input.is-valid~.custom-control-label:before,.was-validated .custom-control-input:valid~.custom-control-label:before{background-color:#9eff3b}.custom-control-input.is-valid~.valid-feedback,.custom-control-input.is-valid~.valid-tooltip,.was-validated .custom-control-input:valid~.valid-feedback,.was-validated .custom-control-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid:checked~.custom-control-label:before,.was-validated .custom-control-input:valid:checked~.custom-control-label:before{background-color:#78ed00}.custom-control-input.is-valid:focus~.custom-control-label:before,.was-validated .custom-control-input:valid:focus~.custom-control-label:before{box-shadow:0 0 0 1px #f5f7fb,0 0 0 2px rgba(94,186,0,.25)}.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#5eba00}.custom-file-input.is-valid~.custom-file-label:before,.was-validated .custom-file-input:valid~.custom-file-label:before{border-color:inherit}.custom-file-input.is-valid~.valid-feedback,.custom-file-input.is-valid~.valid-tooltip,.was-validated .custom-file-input:valid~.valid-feedback,.was-validated .custom-file-input:valid~.valid-tooltip{display:block}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{box-shadow:0 0 0 2px rgba(94,186,0,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:87.5%;color:#cd201f}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.5rem;margin-top:.1rem;font-size:.875rem;line-height:1;color:#fff;background-color:rgba(205,32,31,.8);border-radius:.2rem}.custom-select.is-invalid,.form-control.is-invalid,.was-validated .custom-select:invalid,.was-validated .form-control:invalid{border-color:#cd201f}.custom-select.is-invalid:focus,.form-control.is-invalid:focus,.was-validated .custom-select:invalid:focus,.was-validated .form-control:invalid:focus{border-color:#cd201f;box-shadow:0 0 0 2px rgba(205,32,31,.25)}.custom-select.is-invalid~.invalid-feedback,.custom-select.is-invalid~.invalid-tooltip,.form-control.is-invalid~.invalid-feedback,.form-control.is-invalid~.invalid-tooltip,.was-validated .custom-select:invalid~.invalid-feedback,.was-validated .custom-select:invalid~.invalid-tooltip,.was-validated .form-control:invalid~.invalid-feedback,.was-validated .form-control:invalid~.invalid-tooltip{display:block}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#cd201f}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#cd201f}.custom-control-input.is-invalid~.custom-control-label:before,.was-validated .custom-control-input:invalid~.custom-control-label:before{background-color:#ec8080}.custom-control-input.is-invalid~.invalid-feedback,.custom-control-input.is-invalid~.invalid-tooltip,.was-validated .custom-control-input:invalid~.invalid-feedback,.was-validated .custom-control-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid:checked~.custom-control-label:before,.was-validated .custom-control-input:invalid:checked~.custom-control-label:before{background-color:#e23e3d}.custom-control-input.is-invalid:focus~.custom-control-label:before,.was-validated .custom-control-input:invalid:focus~.custom-control-label:before{box-shadow:0 0 0 1px #f5f7fb,0 0 0 2px rgba(205,32,31,.25)}.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#cd201f}.custom-file-input.is-invalid~.custom-file-label:before,.was-validated .custom-file-input:invalid~.custom-file-label:before{border-color:inherit}.custom-file-input.is-invalid~.invalid-feedback,.custom-file-input.is-invalid~.invalid-tooltip,.was-validated .custom-file-input:invalid~.invalid-feedback,.was-validated .custom-file-input:invalid~.invalid-tooltip{display:block}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{box-shadow:0 0 0 2px rgba(205,32,31,.25)}.form-inline{display:flex;flex-flow:row wrap;align-items:center}.form-inline .form-check{width:100%}@media (min-width:576px){.form-inline label{justify-content:center}.form-inline .form-group,.form-inline label{display:flex;align-items:center;margin-bottom:0}.form-inline .form-group{flex:0 0 auto;flex-flow:row wrap}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .input-group{width:auto}.form-inline .form-check{display:flex;align-items:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{align-items:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;text-align:center;white-space:nowrap;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border:1px solid transparent;padding:.375rem .75rem;font-size:.9375rem;line-height:1.84615385;border-radius:3px;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}.btn:focus,.btn:hover{text-decoration:none}.btn.focus,.btn:focus{outline:0;box-shadow:0 0 0 2px rgba(70,127,207,.25)}.btn.disabled,.btn:disabled{opacity:.65}.btn:not(:disabled):not(.disabled){cursor:pointer}.btn:not(:disabled):not(.disabled).active,.btn:not(:disabled):not(.disabled):active{background-image:none}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#467fcf;border-color:#467fcf}.btn-primary:hover{color:#fff;background-color:#316cbe;border-color:#2f66b3}.btn-primary.focus,.btn-primary:focus{box-shadow:0 0 0 2px rgba(70,127,207,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#467fcf;border-color:#467fcf}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#2f66b3;border-color:#2c60a9}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(70,127,207,.5)}.btn-secondary{color:#fff;background-color:#868e96;border-color:#868e96}.btn-secondary:hover{color:#fff;background-color:#727b84;border-color:#6c757d}.btn-secondary.focus,.btn-secondary:focus{box-shadow:0 0 0 2px rgba(134,142,150,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#868e96;border-color:#868e96}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#666e76}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(134,142,150,.5)}.btn-success{color:#fff;background-color:#5eba00;border-color:#5eba00}.btn-success:hover{color:#fff;background-color:#4b9400;border-color:#448700}.btn-success.focus,.btn-success:focus{box-shadow:0 0 0 2px rgba(94,186,0,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#5eba00;border-color:#5eba00}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#448700;border-color:#3e7a00}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(94,186,0,.5)}.btn-info{color:#fff;background-color:#45aaf2;border-color:#45aaf2}.btn-info:hover{color:#fff;background-color:#219af0;border-color:#1594ef}.btn-info.focus,.btn-info:focus{box-shadow:0 0 0 2px rgba(69,170,242,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#45aaf2;border-color:#45aaf2}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#1594ef;border-color:#108ee7}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(69,170,242,.5)}.btn-warning{color:#fff;background-color:#f1c40f;border-color:#f1c40f}.btn-warning:hover{color:#fff;background-color:#cea70c;border-color:#c29d0b}.btn-warning.focus,.btn-warning:focus{box-shadow:0 0 0 2px rgba(241,196,15,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#fff;background-color:#f1c40f;border-color:#f1c40f}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#fff;background-color:#c29d0b;border-color:#b6940b}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(241,196,15,.5)}.btn-danger{color:#fff;background-color:#cd201f;border-color:#cd201f}.btn-danger:hover{color:#fff;background-color:#ac1b1a;border-color:#a11918}.btn-danger.focus,.btn-danger:focus{box-shadow:0 0 0 2px rgba(205,32,31,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#cd201f;border-color:#cd201f}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#a11918;border-color:#961717}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(205,32,31,.5)}.btn-light{color:#495057;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#495057;background-color:#e2e6ea;border-color:#dae0e5}.btn-light.focus,.btn-light:focus{box-shadow:0 0 0 2px rgba(248,249,250,.5)}.btn-light.disabled,.btn-light:disabled{color:#495057;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#495057;background-color:#dae0e5;border-color:#d3d9df}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(248,249,250,.5)}.btn-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark.focus,.btn-dark:focus{box-shadow:0 0 0 2px rgba(52,58,64,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(52,58,64,.5)}.btn-outline-primary{color:#467fcf;background-color:initial;background-image:none;border-color:#467fcf}.btn-outline-primary:hover{color:#fff;background-color:#467fcf;border-color:#467fcf}.btn-outline-primary.focus,.btn-outline-primary:focus{box-shadow:0 0 0 2px rgba(70,127,207,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#467fcf;background-color:initial}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#467fcf;border-color:#467fcf}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(70,127,207,.5)}.btn-outline-secondary{color:#868e96;background-color:initial;background-image:none;border-color:#868e96}.btn-outline-secondary:hover{color:#fff;background-color:#868e96;border-color:#868e96}.btn-outline-secondary.focus,.btn-outline-secondary:focus{box-shadow:0 0 0 2px rgba(134,142,150,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#868e96;background-color:initial}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#868e96;border-color:#868e96}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(134,142,150,.5)}.btn-outline-success{color:#5eba00;background-color:initial;background-image:none;border-color:#5eba00}.btn-outline-success:hover{color:#fff;background-color:#5eba00;border-color:#5eba00}.btn-outline-success.focus,.btn-outline-success:focus{box-shadow:0 0 0 2px rgba(94,186,0,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#5eba00;background-color:initial}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#5eba00;border-color:#5eba00}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(94,186,0,.5)}.btn-outline-info{color:#45aaf2;background-color:initial;background-image:none;border-color:#45aaf2}.btn-outline-info:hover{color:#fff;background-color:#45aaf2;border-color:#45aaf2}.btn-outline-info.focus,.btn-outline-info:focus{box-shadow:0 0 0 2px rgba(69,170,242,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#45aaf2;background-color:initial}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#45aaf2;border-color:#45aaf2}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(69,170,242,.5)}.btn-outline-warning{color:#f1c40f;background-color:initial;background-image:none;border-color:#f1c40f}.btn-outline-warning:hover{color:#fff;background-color:#f1c40f;border-color:#f1c40f}.btn-outline-warning.focus,.btn-outline-warning:focus{box-shadow:0 0 0 2px rgba(241,196,15,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#f1c40f;background-color:initial}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#fff;background-color:#f1c40f;border-color:#f1c40f}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(241,196,15,.5)}.btn-outline-danger{color:#cd201f;background-color:initial;background-image:none;border-color:#cd201f}.btn-outline-danger:hover{color:#fff;background-color:#cd201f;border-color:#cd201f}.btn-outline-danger.focus,.btn-outline-danger:focus{box-shadow:0 0 0 2px rgba(205,32,31,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#cd201f;background-color:initial}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#cd201f;border-color:#cd201f}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(205,32,31,.5)}.btn-outline-light{color:#f8f9fa;background-color:initial;background-image:none;border-color:#f8f9fa}.btn-outline-light:hover{color:#495057;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.focus,.btn-outline-light:focus{box-shadow:0 0 0 2px rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:initial}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#495057;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(248,249,250,.5)}.btn-outline-dark{color:#343a40;background-color:initial;background-image:none;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.focus,.btn-outline-dark:focus{box-shadow:0 0 0 2px rgba(52,58,64,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:initial}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(52,58,64,.5)}.btn-link{font-weight:400;color:#467fcf;background-color:initial}.btn-link:hover{color:#295a9f;background-color:initial}.btn-link.focus,.btn-link:focus,.btn-link:hover{text-decoration:underline;border-color:transparent}.btn-link.focus,.btn-link:focus{box-shadow:none}.btn-link.disabled,.btn-link:disabled{color:#868e96}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.125rem;line-height:1.625;border-radius:3px}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;line-height:1.33333333;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;transition:opacity .15s linear}.fade.show{opacity:1}.collapse{display:none}.collapse.show{display:block}tr.collapse.show{display:table-row}tbody.collapse.show{display:table-row-group}.collapsing{height:0;overflow:hidden;transition:height .35s ease}.collapsing,.dropdown,.dropup{position:relative}.dropdown-toggle:after{display:inline-block;width:0;height:0;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:.9375rem;color:#495057;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,40,100,.12);border-radius:3px}.dropup .dropdown-menu{margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle:after{display:inline-block;width:0;height:0;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty:after{margin-left:0}.dropright .dropdown-menu{margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle:after{display:inline-block;width:0;height:0;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty:after{margin-left:0}.dropright .dropdown-toggle:after{vertical-align:0}.dropleft .dropdown-menu{margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle:after{display:inline-block;width:0;height:0;margin-left:.255em;vertical-align:.255em;content:"";display:none}.dropleft .dropdown-toggle:before{display:inline-block;width:0;height:0;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty:after{margin-left:0}.dropleft .dropdown-toggle:before{vertical-align:0}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:initial;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#16181b;text-decoration:none;background-color:#f8f9fa}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#467fcf}.dropdown-item.disabled,.dropdown-item:disabled{color:#868e96;background-color:initial}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#868e96;white-space:nowrap}.btn-group,.btn-group-vertical{position:relative;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;flex:0 1 auto}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:1}.btn-group-vertical .btn+.btn,.btn-group-vertical .btn+.btn-group,.btn-group-vertical .btn-group+.btn,.btn-group-vertical .btn-group+.btn-group,.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{display:flex;flex-wrap:wrap;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split:after{margin-left:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{flex-direction:column;align-items:flex-start;justify-content:center}.btn-group-vertical .btn,.btn-group-vertical .btn-group{width:100%}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio],.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:flex;flex-wrap:wrap;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control{position:relative;flex:1 1 auto;width:1%;margin-bottom:0}.input-group>.custom-file:focus,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control{margin-left:-1px}.input-group>.custom-select:not(:last-child),.input-group>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:flex;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label:before{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label,.input-group>.custom-file:not(:first-child) .custom-file-label:before{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-append,.input-group-prepend{display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:flex;align-items:center;padding:.375rem .75rem;margin-bottom:0;font-size:.9375rem;font-weight:400;line-height:1.6;color:#495057;text-align:center;white-space:nowrap;background-color:#fbfbfc;border:1px solid rgba(0,40,100,.12);border-radius:3px}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;display:block;min-height:1.5rem;padding-left:1.5rem}.custom-control-inline{display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;z-index:-1;opacity:0}.custom-control-input:checked~.custom-control-label:before{color:#fff;background-color:#467fcf}.custom-control-input:focus~.custom-control-label:before{box-shadow:0 0 0 1px #f5f7fb,0 0 0 2px rgba(70,127,207,.25)}.custom-control-input:active~.custom-control-label:before{color:#fff;background-color:#d4e1f4}.custom-control-input:disabled~.custom-control-label{color:#868e96}.custom-control-input:disabled~.custom-control-label:before{background-color:#e9ecef}.custom-control-label{margin-bottom:0}.custom-control-label:before{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:#dee2e6}.custom-control-label:after,.custom-control-label:before{position:absolute;top:.25rem;left:0;display:block;width:1rem;height:1rem;content:""}.custom-control-label:after{background-repeat:no-repeat;background-position:50%;background-size:50% 50%}.custom-checkbox .custom-control-label:before{border-radius:3px}.custom-checkbox .custom-control-input:checked~.custom-control-label:before{background-color:#467fcf}.custom-checkbox .custom-control-input:checked~.custom-control-label:after{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label:before{background-color:#467fcf}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label:after{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3E%3Cpath stroke='%23fff' d='M0 2h4'/%3E%3C/svg%3E")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label:before{background-color:rgba(70,127,207,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label:before{background-color:rgba(70,127,207,.5)}.custom-radio .custom-control-label:before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label:before{background-color:#467fcf}.custom-radio .custom-control-input:checked~.custom-control-label:after{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23fff'/%3E%3C/svg%3E")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label:before{background-color:rgba(70,127,207,.5)}.custom-select{display:inline-block;width:100%;height:2.375rem;padding:.5rem 1.75rem .5rem .75rem;line-height:1.5;color:#495057;vertical-align:middle;background:#fff url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 5'%3E%3Cpath fill='#999' d='M0 0L10 0L5 5L0 0'/%3E%3C/svg%3E") no-repeat right .75rem center;background-size:8px 10px;border:1px solid rgba(0,40,100,.12);border-radius:3px;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#1991eb;outline:0;box-shadow:inset 0 1px 2px rgba(0,0,0,.075),0 0 5px rgba(25,145,235,.5)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#868e96;background-color:#e9ecef}.custom-select::-ms-expand{opacity:0}.custom-select-sm{height:calc(1.8125rem + 2px);font-size:75%}.custom-select-lg,.custom-select-sm{padding-top:.5rem;padding-bottom:.5rem}.custom-select-lg{height:calc(2.6875rem + 2px);font-size:125%}.custom-file{display:inline-block;margin-bottom:0}.custom-file,.custom-file-input{position:relative;width:100%;height:2.375rem}.custom-file-input{z-index:2;margin:0;opacity:0}.custom-file-input:focus~.custom-file-control{border-color:#1991eb;box-shadow:0 0 0 2px rgba(70,127,207,.25)}.custom-file-input:focus~.custom-file-control:before{border-color:#1991eb}.custom-file-input:lang(en)~.custom-file-label:after{content:"Browse"}.custom-file-label{left:0;z-index:1;height:2.375rem;background-color:#fff;border:1px solid rgba(0,40,100,.12);border-radius:3px}.custom-file-label,.custom-file-label:after{position:absolute;top:0;right:0;padding:.375rem .75rem;line-height:1.5;color:#495057}.custom-file-label:after{bottom:0;z-index:3;display:block;height:calc(2.375rem - 2px);content:"Browse";background-color:#fbfbfc;border-left:1px solid rgba(0,40,100,.12);border-radius:0 3px 3px 0}.nav{display:flex;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#868e96}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#868e96;background-color:initial;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#f5f7fb;border-color:#dee2e6 #dee2e6 #f5f7fb}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:3px}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#467fcf}.nav-fill .nav-item{flex:1 1 auto;text-align:center}.nav-justified .nav-item{flex-basis:0;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;padding:.5rem 1rem}.navbar,.navbar>.container,.navbar>.container-fluid{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.359375rem;padding-bottom:.359375rem;margin-right:1rem;font-size:1.125rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:flex;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{flex-basis:100%;flex-grow:1;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.125rem;line-height:1;background-color:initial;border:1px solid transparent;border-radius:3px}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler:not(:disabled):not(.disabled){cursor:pointer}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat 50%;background-size:100% 100%}@media (max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:576px){.navbar-expand-sm{flex-flow:row nowrap;justify-content:flex-start}.navbar-expand-sm .navbar-nav{flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .dropdown-menu-right{right:0;left:auto}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}.navbar-expand-sm .dropup .dropdown-menu{top:auto;bottom:100%}}@media (max-width:767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:768px){.navbar-expand-md{flex-flow:row nowrap;justify-content:flex-start}.navbar-expand-md .navbar-nav{flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .dropdown-menu-right{right:0;left:auto}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}.navbar-expand-md .dropup .dropdown-menu{top:auto;bottom:100%}}@media (max-width:991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:992px){.navbar-expand-lg{flex-flow:row nowrap;justify-content:flex-start}.navbar-expand-lg .navbar-nav{flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .dropdown-menu-right{right:0;left:auto}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}.navbar-expand-lg .dropup .dropdown-menu{top:auto;bottom:100%}}@media (max-width:1279.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:1280px){.navbar-expand-xl{flex-flow:row nowrap;justify-content:flex-start}.navbar-expand-xl .navbar-nav{flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .dropdown-menu-right{right:0;left:auto}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}.navbar-expand-xl .dropup .dropdown-menu{top:auto;bottom:100%}}.navbar-expand{flex-flow:row nowrap;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .dropdown-menu-right{right:0;left:auto}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid{flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-expand .dropup .dropdown-menu{top:auto;bottom:100%}.navbar-light .navbar-brand,.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a,.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand,.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:hsla(0,0%,100%,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:hsla(0,0%,100%,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:hsla(0,0%,100%,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:hsla(0,0%,100%,.5);border-color:hsla(0,0%,100%,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E")}.navbar-dark .navbar-text{color:hsla(0,0%,100%,.5)}.navbar-dark .navbar-text a,.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{display:flex;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:initial;border:1px solid rgba(0,40,100,.12);border-radius:3px}.card>hr{margin-right:0;margin-left:0}.card>.list-group:first-child .list-group-item:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.card>.list-group:last-child .list-group-item:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.card-subtitle{margin-top:-.75rem}.card-subtitle,.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.5rem}.card-header{padding:1.5rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,40,100,.12)}.card-header:first-child{border-radius:2px 2px 0 0}.card-header+.list-group .list-group-item:first-child{border-top:0}.card-footer{padding:1.5rem;background-color:rgba(0,0,0,.03)}.card-footer:last-child{border-radius:0 0 2px 2px}.card-header-tabs{margin-bottom:-1.5rem}.card-header-pills,.card-header-tabs{margin-right:-.75rem;margin-left:-.75rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img{width:100%;border-radius:2px}.card-img-top{width:100%;border-top-left-radius:2px;border-top-right-radius:2px}.card-img-bottom{width:100%;border-bottom-right-radius:2px;border-bottom-left-radius:2px}.card-deck{display:flex;flex-direction:column}.card-deck .card{margin-bottom:.75rem}@media (min-width:576px){.card-deck{flex-flow:row wrap;margin-right:-.75rem;margin-left:-.75rem}.card-deck .card{display:flex;flex:1 0;flex-direction:column;margin-right:.75rem;margin-bottom:0;margin-left:.75rem}}.card-group{display:flex;flex-direction:column}.card-group>.card{margin-bottom:.75rem}@media (min-width:576px){.card-group{flex-flow:row wrap}.card-group>.card{flex:1 0;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:first-child{border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:first-child .card-header,.card-group>.card:first-child .card-img-top{border-top-right-radius:0}.card-group>.card:first-child .card-footer,.card-group>.card:first-child .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:last-child{border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:last-child .card-header,.card-group>.card:last-child .card-img-top{border-top-left-radius:0}.card-group>.card:last-child .card-footer,.card-group>.card:last-child .card-img-bottom{border-bottom-left-radius:0}.card-group>.card:only-child{border-radius:3px}.card-group>.card:only-child .card-header,.card-group>.card:only-child .card-img-top{border-top-left-radius:3px;border-top-right-radius:3px}.card-group>.card:only-child .card-footer,.card-group>.card:only-child .card-img-bottom{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.card-group>.card:not(:first-child):not(:last-child):not(:only-child),.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-footer,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-header,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-img-bottom,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-img-top{border-radius:0}}.card-columns .card{margin-bottom:1.5rem}@media (min-width:576px){.card-columns{-webkit-column-count:3;column-count:3;-webkit-column-gap:1.25rem;-moz-column-gap:1.25rem;grid-column-gap:1.25rem;column-gap:1.25rem}.card-columns .card{display:inline-block;width:100%}}.breadcrumb{display:flex;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:3px}.breadcrumb-item+.breadcrumb-item:before{display:inline-block;padding-right:.5rem;padding-left:.5rem;color:#868e96;content:"/"}.breadcrumb-item+.breadcrumb-item:hover:before{text-decoration:underline;text-decoration:none}.breadcrumb-item.active{color:#868e96}.pagination{display:flex;padding-left:0;list-style:none;border-radius:3px}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#495057;background-color:#fff;border:1px solid #dee2e6}.page-link:hover{color:#295a9f;text-decoration:none;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:2;outline:0;box-shadow:0 0 0 2px rgba(70,127,207,.25)}.page-link:not(:disabled):not(.disabled){cursor:pointer}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:3px;border-bottom-left-radius:3px}.page-item:last-child .page-link{border-top-right-radius:3px;border-bottom-right-radius:3px}.page-item.active .page-link{z-index:1;color:#fff;background-color:#467fcf;border-color:#467fcf}.page-item.disabled .page-link{color:#ced4da;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dee2e6}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.125rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:3px;border-bottom-right-radius:3px}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:3px;border-bottom-right-radius:3px}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:600;line-height:1;text-align:center;white-space:nowrap;vertical-align:initial;border-radius:3px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#467fcf}.badge-primary[href]:focus,.badge-primary[href]:hover{color:#fff;text-decoration:none;background-color:#2f66b3}.badge-secondary{color:#fff;background-color:#868e96}.badge-secondary[href]:focus,.badge-secondary[href]:hover{color:#fff;text-decoration:none;background-color:#6c757d}.badge-success{color:#fff;background-color:#5eba00}.badge-success[href]:focus,.badge-success[href]:hover{color:#fff;text-decoration:none;background-color:#448700}.badge-info{color:#fff;background-color:#45aaf2}.badge-info[href]:focus,.badge-info[href]:hover{color:#fff;text-decoration:none;background-color:#1594ef}.badge-warning{color:#fff;background-color:#f1c40f}.badge-warning[href]:focus,.badge-warning[href]:hover{color:#fff;text-decoration:none;background-color:#c29d0b}.badge-danger{color:#fff;background-color:#cd201f}.badge-danger[href]:focus,.badge-danger[href]:hover{color:#fff;text-decoration:none;background-color:#a11918}.badge-light{color:#495057;background-color:#f8f9fa}.badge-light[href]:focus,.badge-light[href]:hover{color:#495057;text-decoration:none;background-color:#dae0e5}.badge-dark{color:#fff;background-color:#343a40}.badge-dark[href]:focus,.badge-dark[href]:hover{color:#fff;text-decoration:none;background-color:#1d2124}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:3px}@media (min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:3px}.alert-heading{color:inherit}.alert-link{font-weight:600}.alert-dismissible{padding-right:3.90625rem}.alert-dismissible .close{position:absolute;top:0;right:0;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#24426c;background-color:#dae5f5;border-color:#cbdbf2}.alert-primary hr{border-top-color:#b7cded}.alert-primary .alert-link{color:#172b46}.alert-secondary{color:#464a4e;background-color:#e7e8ea;border-color:#dddfe2}.alert-secondary hr{border-top-color:#cfd2d6}.alert-secondary .alert-link{color:#2e3133}.alert-success{color:#316100;background-color:#dff1cc;border-color:#d2ecb8}.alert-success hr{border-top-color:#c5e7a4}.alert-success .alert-link{color:#172e00}.alert-info{color:#24587e;background-color:#daeefc;border-color:#cbe7fb}.alert-info hr{border-top-color:#b3dcf9}.alert-info .alert-link{color:#193c56}.alert-warning{color:#7d6608;background-color:#fcf3cf;border-color:#fbeebc}.alert-warning hr{border-top-color:#fae8a4}.alert-warning .alert-link{color:#4d3f05}.alert-danger{color:#6b1110;background-color:#f5d2d2;border-color:#f1c1c0}.alert-danger hr{border-top-color:#ecacab}.alert-danger .alert-link{color:#3f0a09}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}@-webkit-keyframes progress-bar-stripes{0%{background-position:1rem 0}to{background-position:0 0}}@keyframes progress-bar-stripes{0%{background-position:1rem 0}to{background-position:0 0}}.progress{display:flex;height:1rem;overflow:hidden;font-size:.703125rem;background-color:#e9ecef;border-radius:3px}.progress-bar{display:flex;flex-direction:column;justify-content:center;color:#fff;text-align:center;background-color:#467fcf;transition:width .6s ease}.progress-bar-striped{background-image:linear-gradient(45deg,hsla(0,0%,100%,.15) 25%,transparent 0,transparent 50%,hsla(0,0%,100%,.15) 0,hsla(0,0%,100%,.15) 75%,transparent 0,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:progress-bar-stripes 1s linear infinite;animation:progress-bar-stripes 1s linear infinite}.media{display:flex;align-items:flex-start}.media-body{flex:1 1}.list-group{display:flex;flex-direction:column;padding-left:0;margin-bottom:0}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#495057;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;margin-bottom:-1px;background-color:#fff;border:1px solid rgba(0,40,100,.12)}.list-group-item:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.list-group-item:focus,.list-group-item:hover{z-index:1;text-decoration:none}.list-group-item.disabled,.list-group-item:disabled{color:#868e96;background-color:#fff}.list-group-item.active{z-index:2;color:#467fcf;background-color:#f8fafd;border-color:rgba(0,40,100,.12)}.list-group-flush .list-group-item{border-right:0;border-left:0;border-radius:0}.list-group-flush:first-child .list-group-item:first-child{border-top:0}.list-group-flush:last-child .list-group-item:last-child{border-bottom:0}.list-group-item-primary{color:#24426c;background-color:#cbdbf2}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#24426c;background-color:#b7cded}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#24426c;border-color:#24426c}.list-group-item-secondary{color:#464a4e;background-color:#dddfe2}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#464a4e;background-color:#cfd2d6}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#464a4e;border-color:#464a4e}.list-group-item-success{color:#316100;background-color:#d2ecb8}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#316100;background-color:#c5e7a4}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#316100;border-color:#316100}.list-group-item-info{color:#24587e;background-color:#cbe7fb}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#24587e;background-color:#b3dcf9}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#24587e;border-color:#24587e}.list-group-item-warning{color:#7d6608;background-color:#fbeebc}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#7d6608;background-color:#fae8a4}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#7d6608;border-color:#7d6608}.list-group-item-danger{color:#6b1110;background-color:#f1c1c0}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#6b1110;background-color:#ecacab}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#6b1110;border-color:#6b1110}.list-group-item-light{color:#818182;background-color:#fdfdfe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#818182;background-color:#ececf6}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#818182;border-color:#818182}.list-group-item-dark{color:#1b1e21;background-color:#c6c8ca}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#1b1e21;background-color:#b9bbbe}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1b1e21;border-color:#1b1e21}.close{float:right;font-size:1.40625rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:focus,.close:hover{color:#000;text-decoration:none;opacity:.75}.close:not(:disabled):not(.disabled){cursor:pointer}button.close{padding:0;background-color:initial;border:0;-webkit-appearance:none}.modal,.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;outline:0}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out;-webkit-transform:translateY(-25%);transform:translateY(-25%)}.modal.show .modal-dialog{-webkit-transform:translate(0);transform:translate(0)}.modal-dialog-centered{display:flex;align-items:center;min-height:calc(100% - 1rem)}.modal-content{position:relative;display:flex;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:3px;outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:flex;align-items:flex-start;justify-content:space-between;padding:1rem;border-bottom:1px solid #e9ecef;border-top-left-radius:3px;border-top-right-radius:3px}.modal-header .close{padding:1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;flex:1 1 auto;padding:1rem}.modal-footer{display:flex;align-items:center;justify-content:flex-end;padding:1rem;border-top:1px solid #e9ecef}.modal-footer>:not(:first-child){margin-left:.25rem}.modal-footer>:not(:last-child){margin-right:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg{max-width:800px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:Source Sans Pro,-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica Neue,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow:before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow:before,.bs-tooltip-top .arrow:before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow:before,.bs-tooltip-right .arrow:before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow:before,.bs-tooltip-bottom .arrow:before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow:before,.bs-tooltip-left .arrow:before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:3px}.popover{top:0;left:0;z-index:1060;max-width:276px;font-family:Source Sans Pro,-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica Neue,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid #dee3eb;border-radius:3px}.popover,.popover .arrow{position:absolute;display:block}.popover .arrow{width:.5rem;height:.5rem;margin:0 3px}.popover .arrow:after,.popover .arrow:before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top] .arrow,.bs-popover-top .arrow{bottom:calc(-.5rem + -1px)}.bs-popover-auto[x-placement^=top] .arrow:after,.bs-popover-auto[x-placement^=top] .arrow:before,.bs-popover-top .arrow:after,.bs-popover-top .arrow:before{border-width:.5rem .25rem 0}.bs-popover-auto[x-placement^=top] .arrow:before,.bs-popover-top .arrow:before{bottom:0;border-top-color:#dee3eb}.bs-popover-auto[x-placement^=top] .arrow:after,.bs-popover-top .arrow:after{bottom:1px;border-top-color:#fff}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right] .arrow,.bs-popover-right .arrow{left:calc(-.5rem + -1px);width:.5rem;height:.5rem;margin:3px 0}.bs-popover-auto[x-placement^=right] .arrow:after,.bs-popover-auto[x-placement^=right] .arrow:before,.bs-popover-right .arrow:after,.bs-popover-right .arrow:before{border-width:.25rem .5rem .25rem 0}.bs-popover-auto[x-placement^=right] .arrow:before,.bs-popover-right .arrow:before{left:0;border-right-color:#dee3eb}.bs-popover-auto[x-placement^=right] .arrow:after,.bs-popover-right .arrow:after{left:1px;border-right-color:#fff}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom] .arrow,.bs-popover-bottom .arrow{top:calc(-.5rem + -1px)}.bs-popover-auto[x-placement^=bottom] .arrow:after,.bs-popover-auto[x-placement^=bottom] .arrow:before,.bs-popover-bottom .arrow:after,.bs-popover-bottom .arrow:before{border-width:0 .25rem .5rem}.bs-popover-auto[x-placement^=bottom] .arrow:before,.bs-popover-bottom .arrow:before{top:0;border-bottom-color:#dee3eb}.bs-popover-auto[x-placement^=bottom] .arrow:after,.bs-popover-bottom .arrow:after{top:1px;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header:before,.bs-popover-bottom .popover-header:before{position:absolute;top:0;left:50%;display:block;width:.5rem;margin-left:-.25rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left] .arrow,.bs-popover-left .arrow{right:calc(-.5rem + -1px);width:.5rem;height:.5rem;margin:3px 0}.bs-popover-auto[x-placement^=left] .arrow:after,.bs-popover-auto[x-placement^=left] .arrow:before,.bs-popover-left .arrow:after,.bs-popover-left .arrow:before{border-width:.25rem 0 .25rem .5rem}.bs-popover-auto[x-placement^=left] .arrow:before,.bs-popover-left .arrow:before{right:0;border-left-color:#dee3eb}.bs-popover-auto[x-placement^=left] .arrow:after,.bs-popover-left .arrow:after{right:1px;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:.9375rem;color:inherit;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:2px;border-top-right-radius:2px}.popover-header:empty{display:none}.popover-body{padding:.75rem 1rem;color:#6e7687}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-item{position:relative;display:none;align-items:center;width:100%;transition:-webkit-transform .6s ease;transition:transform .6s ease;transition:transform .6s ease,-webkit-transform .6s ease;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.carousel-item-next,.carousel-item-prev{position:absolute;top:0}.carousel-item-next.carousel-item-left,.carousel-item-prev.carousel-item-right{-webkit-transform:translateX(0);transform:translateX(0)}@supports (((-webkit-transform-style:preserve-3d) or (transform-style:preserve-3d))){.carousel-item-next.carousel-item-left,.carousel-item-prev.carousel-item-right{-webkit-transform:translateZ(0);transform:translateZ(0)}}.active.carousel-item-right,.carousel-item-next{-webkit-transform:translateX(100%);transform:translateX(100%)}@supports (((-webkit-transform-style:preserve-3d) or (transform-style:preserve-3d))){.active.carousel-item-right,.carousel-item-next{-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}}.active.carousel-item-left,.carousel-item-prev{-webkit-transform:translateX(-100%);transform:translateX(-100%)}@supports (((-webkit-transform-style:preserve-3d) or (transform-style:preserve-3d))){.active.carousel-item-left,.carousel-item-prev{-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;display:flex;align-items:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:.5}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:transparent no-repeat 50%;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3E%3C/svg%3E")}.carousel-control-next-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3E%3C/svg%3E")}.carousel-indicators{position:absolute;right:0;bottom:10px;left:0;z-index:15;display:flex;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{position:relative;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;background-color:hsla(0,0%,100%,.5)}.carousel-indicators li:before{top:-10px}.carousel-indicators li:after,.carousel-indicators li:before{position:absolute;left:0;display:inline-block;width:100%;height:10px;content:""}.carousel-indicators li:after{bottom:-10px}.carousel-indicators .active{background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}.align-baseline{vertical-align:initial!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#467fcf!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#2f66b3!important}.bg-secondary{background-color:#868e96!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#6c757d!important}.bg-success{background-color:#5eba00!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#448700!important}.bg-info{background-color:#45aaf2!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#1594ef!important}.bg-warning{background-color:#f1c40f!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#c29d0b!important}.bg-danger{background-color:#cd201f!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#a11918!important}.bg-light{background-color:#f8f9fa!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#dae0e5!important}.bg-dark{background-color:#343a40!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#1d2124!important}.bg-transparent{background-color:initial!important}.border{border:1px solid rgba(0,40,100,.12)!important}.border-top{border-top:1px solid rgba(0,40,100,.12)!important}.border-right{border-right:1px solid rgba(0,40,100,.12)!important}.border-bottom{border-bottom:1px solid rgba(0,40,100,.12)!important}.border-left{border-left:1px solid rgba(0,40,100,.12)!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#467fcf!important}.border-secondary{border-color:#868e96!important}.border-success{border-color:#5eba00!important}.border-info{border-color:#45aaf2!important}.border-warning{border-color:#f1c40f!important}.border-danger{border-color:#cd201f!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#343a40!important}.border-white{border-color:#fff!important}.rounded{border-radius:3px!important}.rounded-top{border-top-left-radius:3px!important}.rounded-right,.rounded-top{border-top-right-radius:3px!important}.rounded-bottom,.rounded-right{border-bottom-right-radius:3px!important}.rounded-bottom,.rounded-left{border-bottom-left-radius:3px!important}.rounded-left{border-top-left-radius:3px!important}.rounded-circle{border-radius:50%!important}.rounded-0{border-radius:0!important}.clearfix:after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:flex!important}.d-inline-flex{display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:flex!important}.d-sm-inline-flex{display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:flex!important}.d-md-inline-flex{display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:flex!important}.d-lg-inline-flex{display:inline-flex!important}}@media (min-width:1280px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:flex!important}.d-xl-inline-flex{display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:flex!important}.d-print-inline-flex{display:inline-flex!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive:before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9:before{padding-top:42.85714286%}.embed-responsive-16by9:before{padding-top:56.25%}.embed-responsive-4by3:before{padding-top:75%}.embed-responsive-1by1:before{padding-top:100%}.flex-row{flex-direction:row!important}.flex-column{flex-direction:column!important}.flex-row-reverse{flex-direction:row-reverse!important}.flex-column-reverse{flex-direction:column-reverse!important}.flex-wrap{flex-wrap:wrap!important}.flex-nowrap{flex-wrap:nowrap!important}.flex-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-start{justify-content:flex-start!important}.justify-content-end{justify-content:flex-end!important}.justify-content-center{justify-content:center!important}.justify-content-between{justify-content:space-between!important}.justify-content-around{justify-content:space-around!important}.align-items-start{align-items:flex-start!important}.align-items-end{align-items:flex-end!important}.align-items-center{align-items:center!important}.align-items-baseline{align-items:baseline!important}.align-items-stretch{align-items:stretch!important}.align-content-start{align-content:flex-start!important}.align-content-end{align-content:flex-end!important}.align-content-center{align-content:center!important}.align-content-between{align-content:space-between!important}.align-content-around{align-content:space-around!important}.align-content-stretch{align-content:stretch!important}.align-self-auto{align-self:auto!important}.align-self-start{align-self:flex-start!important}.align-self-end{align-self:flex-end!important}.align-self-center{align-self:center!important}.align-self-baseline{align-self:baseline!important}.align-self-stretch{align-self:stretch!important}@media (min-width:576px){.flex-sm-row{flex-direction:row!important}.flex-sm-column{flex-direction:column!important}.flex-sm-row-reverse{flex-direction:row-reverse!important}.flex-sm-column-reverse{flex-direction:column-reverse!important}.flex-sm-wrap{flex-wrap:wrap!important}.flex-sm-nowrap{flex-wrap:nowrap!important}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-sm-start{justify-content:flex-start!important}.justify-content-sm-end{justify-content:flex-end!important}.justify-content-sm-center{justify-content:center!important}.justify-content-sm-between{justify-content:space-between!important}.justify-content-sm-around{justify-content:space-around!important}.align-items-sm-start{align-items:flex-start!important}.align-items-sm-end{align-items:flex-end!important}.align-items-sm-center{align-items:center!important}.align-items-sm-baseline{align-items:baseline!important}.align-items-sm-stretch{align-items:stretch!important}.align-content-sm-start{align-content:flex-start!important}.align-content-sm-end{align-content:flex-end!important}.align-content-sm-center{align-content:center!important}.align-content-sm-between{align-content:space-between!important}.align-content-sm-around{align-content:space-around!important}.align-content-sm-stretch{align-content:stretch!important}.align-self-sm-auto{align-self:auto!important}.align-self-sm-start{align-self:flex-start!important}.align-self-sm-end{align-self:flex-end!important}.align-self-sm-center{align-self:center!important}.align-self-sm-baseline{align-self:baseline!important}.align-self-sm-stretch{align-self:stretch!important}}@media (min-width:768px){.flex-md-row{flex-direction:row!important}.flex-md-column{flex-direction:column!important}.flex-md-row-reverse{flex-direction:row-reverse!important}.flex-md-column-reverse{flex-direction:column-reverse!important}.flex-md-wrap{flex-wrap:wrap!important}.flex-md-nowrap{flex-wrap:nowrap!important}.flex-md-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-md-start{justify-content:flex-start!important}.justify-content-md-end{justify-content:flex-end!important}.justify-content-md-center{justify-content:center!important}.justify-content-md-between{justify-content:space-between!important}.justify-content-md-around{justify-content:space-around!important}.align-items-md-start{align-items:flex-start!important}.align-items-md-end{align-items:flex-end!important}.align-items-md-center{align-items:center!important}.align-items-md-baseline{align-items:baseline!important}.align-items-md-stretch{align-items:stretch!important}.align-content-md-start{align-content:flex-start!important}.align-content-md-end{align-content:flex-end!important}.align-content-md-center{align-content:center!important}.align-content-md-between{align-content:space-between!important}.align-content-md-around{align-content:space-around!important}.align-content-md-stretch{align-content:stretch!important}.align-self-md-auto{align-self:auto!important}.align-self-md-start{align-self:flex-start!important}.align-self-md-end{align-self:flex-end!important}.align-self-md-center{align-self:center!important}.align-self-md-baseline{align-self:baseline!important}.align-self-md-stretch{align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{flex-direction:row!important}.flex-lg-column{flex-direction:column!important}.flex-lg-row-reverse{flex-direction:row-reverse!important}.flex-lg-column-reverse{flex-direction:column-reverse!important}.flex-lg-wrap{flex-wrap:wrap!important}.flex-lg-nowrap{flex-wrap:nowrap!important}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-lg-start{justify-content:flex-start!important}.justify-content-lg-end{justify-content:flex-end!important}.justify-content-lg-center{justify-content:center!important}.justify-content-lg-between{justify-content:space-between!important}.justify-content-lg-around{justify-content:space-around!important}.align-items-lg-start{align-items:flex-start!important}.align-items-lg-end{align-items:flex-end!important}.align-items-lg-center{align-items:center!important}.align-items-lg-baseline{align-items:baseline!important}.align-items-lg-stretch{align-items:stretch!important}.align-content-lg-start{align-content:flex-start!important}.align-content-lg-end{align-content:flex-end!important}.align-content-lg-center{align-content:center!important}.align-content-lg-between{align-content:space-between!important}.align-content-lg-around{align-content:space-around!important}.align-content-lg-stretch{align-content:stretch!important}.align-self-lg-auto{align-self:auto!important}.align-self-lg-start{align-self:flex-start!important}.align-self-lg-end{align-self:flex-end!important}.align-self-lg-center{align-self:center!important}.align-self-lg-baseline{align-self:baseline!important}.align-self-lg-stretch{align-self:stretch!important}}@media (min-width:1280px){.flex-xl-row{flex-direction:row!important}.flex-xl-column{flex-direction:column!important}.flex-xl-row-reverse{flex-direction:row-reverse!important}.flex-xl-column-reverse{flex-direction:column-reverse!important}.flex-xl-wrap{flex-wrap:wrap!important}.flex-xl-nowrap{flex-wrap:nowrap!important}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-xl-start{justify-content:flex-start!important}.justify-content-xl-end{justify-content:flex-end!important}.justify-content-xl-center{justify-content:center!important}.justify-content-xl-between{justify-content:space-between!important}.justify-content-xl-around{justify-content:space-around!important}.align-items-xl-start{align-items:flex-start!important}.align-items-xl-end{align-items:flex-end!important}.align-items-xl-center{align-items:center!important}.align-items-xl-baseline{align-items:baseline!important}.align-items-xl-stretch{align-items:stretch!important}.align-content-xl-start{align-content:flex-start!important}.align-content-xl-end{align-content:flex-end!important}.align-content-xl-center{align-content:center!important}.align-content-xl-between{align-content:space-between!important}.align-content-xl-around{align-content:space-around!important}.align-content-xl-stretch{align-content:stretch!important}.align-self-xl-auto{align-self:auto!important}.align-self-xl-start{align-self:flex-start!important}.align-self-xl-end{align-self:flex-end!important}.align-self-xl-center{align-self:center!important}.align-self-xl-baseline{align-self:baseline!important}.align-self-xl-stretch{align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:992px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media (min-width:1280px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.fixed-top{top:0}.fixed-bottom,.fixed-top{position:fixed;right:0;left:0;z-index:1030}.fixed-bottom{bottom:0}@supports ((position:-webkit-sticky) or (position:sticky)){.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;-webkit-clip-path:inset(50%);clip-path:inset(50%);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal;-webkit-clip-path:none;clip-path:none}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-0{width:0!important}.w-1{width:.25rem!important}.w-2{width:.5rem!important}.w-3{width:.75rem!important}.w-4{width:1rem!important}.w-5{width:1.5rem!important}.w-6{width:2rem!important}.w-7{width:3rem!important}.w-8{width:4rem!important}.w-9{width:6rem!important}.w-auto{width:auto!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-0{height:0!important}.h-1{height:.25rem!important}.h-2{height:.5rem!important}.h-3{height:.75rem!important}.h-4{height:1rem!important}.h-5{height:1.5rem!important}.h-6{height:2rem!important}.h-7{height:3rem!important}.h-8{height:4rem!important}.h-9{height:6rem!important}.h-auto{height:auto!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:.75rem!important}.mt-3,.my-3{margin-top:.75rem!important}.mr-3,.mx-3{margin-right:.75rem!important}.mb-3,.my-3{margin-bottom:.75rem!important}.ml-3,.mx-3{margin-left:.75rem!important}.m-4{margin:1rem!important}.mt-4,.my-4{margin-top:1rem!important}.mr-4,.mx-4{margin-right:1rem!important}.mb-4,.my-4{margin-bottom:1rem!important}.ml-4,.mx-4{margin-left:1rem!important}.m-5{margin:1.5rem!important}.mt-5,.my-5{margin-top:1.5rem!important}.mr-5,.mx-5{margin-right:1.5rem!important}.mb-5,.my-5{margin-bottom:1.5rem!important}.ml-5,.mx-5{margin-left:1.5rem!important}.m-6{margin:2rem!important}.mt-6,.my-6{margin-top:2rem!important}.mr-6,.mx-6{margin-right:2rem!important}.mb-6,.my-6{margin-bottom:2rem!important}.ml-6,.mx-6{margin-left:2rem!important}.m-7{margin:3rem!important}.mt-7,.my-7{margin-top:3rem!important}.mr-7,.mx-7{margin-right:3rem!important}.mb-7,.my-7{margin-bottom:3rem!important}.ml-7,.mx-7{margin-left:3rem!important}.m-8{margin:4rem!important}.mt-8,.my-8{margin-top:4rem!important}.mr-8,.mx-8{margin-right:4rem!important}.mb-8,.my-8{margin-bottom:4rem!important}.ml-8,.mx-8{margin-left:4rem!important}.m-9{margin:6rem!important}.mt-9,.my-9{margin-top:6rem!important}.mr-9,.mx-9{margin-right:6rem!important}.mb-9,.my-9{margin-bottom:6rem!important}.ml-9,.mx-9{margin-left:6rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:.75rem!important}.pt-3,.py-3{padding-top:.75rem!important}.pr-3,.px-3{padding-right:.75rem!important}.pb-3,.py-3{padding-bottom:.75rem!important}.pl-3,.px-3{padding-left:.75rem!important}.p-4{padding:1rem!important}.pt-4,.py-4{padding-top:1rem!important}.pr-4,.px-4{padding-right:1rem!important}.pb-4,.py-4{padding-bottom:1rem!important}.pl-4,.px-4{padding-left:1rem!important}.p-5{padding:1.5rem!important}.pt-5,.py-5{padding-top:1.5rem!important}.pr-5,.px-5{padding-right:1.5rem!important}.pb-5,.py-5{padding-bottom:1.5rem!important}.pl-5,.px-5{padding-left:1.5rem!important}.p-6{padding:2rem!important}.pt-6,.py-6{padding-top:2rem!important}.pr-6,.px-6{padding-right:2rem!important}.pb-6,.py-6{padding-bottom:2rem!important}.pl-6,.px-6{padding-left:2rem!important}.p-7{padding:3rem!important}.pt-7,.py-7{padding-top:3rem!important}.pr-7,.px-7{padding-right:3rem!important}.pb-7,.py-7{padding-bottom:3rem!important}.pl-7,.px-7{padding-left:3rem!important}.p-8{padding:4rem!important}.pt-8,.py-8{padding-top:4rem!important}.pr-8,.px-8{padding-right:4rem!important}.pb-8,.py-8{padding-bottom:4rem!important}.pl-8,.px-8{padding-left:4rem!important}.p-9{padding:6rem!important}.pt-9,.py-9{padding-top:6rem!important}.pr-9,.px-9{padding-right:6rem!important}.pb-9,.py-9{padding-bottom:6rem!important}.pl-9,.px-9{padding-left:6rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:.75rem!important}.mt-sm-3,.my-sm-3{margin-top:.75rem!important}.mr-sm-3,.mx-sm-3{margin-right:.75rem!important}.mb-sm-3,.my-sm-3{margin-bottom:.75rem!important}.ml-sm-3,.mx-sm-3{margin-left:.75rem!important}.m-sm-4{margin:1rem!important}.mt-sm-4,.my-sm-4{margin-top:1rem!important}.mr-sm-4,.mx-sm-4{margin-right:1rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1rem!important}.ml-sm-4,.mx-sm-4{margin-left:1rem!important}.m-sm-5{margin:1.5rem!important}.mt-sm-5,.my-sm-5{margin-top:1.5rem!important}.mr-sm-5,.mx-sm-5{margin-right:1.5rem!important}.mb-sm-5,.my-sm-5{margin-bottom:1.5rem!important}.ml-sm-5,.mx-sm-5{margin-left:1.5rem!important}.m-sm-6{margin:2rem!important}.mt-sm-6,.my-sm-6{margin-top:2rem!important}.mr-sm-6,.mx-sm-6{margin-right:2rem!important}.mb-sm-6,.my-sm-6{margin-bottom:2rem!important}.ml-sm-6,.mx-sm-6{margin-left:2rem!important}.m-sm-7{margin:3rem!important}.mt-sm-7,.my-sm-7{margin-top:3rem!important}.mr-sm-7,.mx-sm-7{margin-right:3rem!important}.mb-sm-7,.my-sm-7{margin-bottom:3rem!important}.ml-sm-7,.mx-sm-7{margin-left:3rem!important}.m-sm-8{margin:4rem!important}.mt-sm-8,.my-sm-8{margin-top:4rem!important}.mr-sm-8,.mx-sm-8{margin-right:4rem!important}.mb-sm-8,.my-sm-8{margin-bottom:4rem!important}.ml-sm-8,.mx-sm-8{margin-left:4rem!important}.m-sm-9{margin:6rem!important}.mt-sm-9,.my-sm-9{margin-top:6rem!important}.mr-sm-9,.mx-sm-9{margin-right:6rem!important}.mb-sm-9,.my-sm-9{margin-bottom:6rem!important}.ml-sm-9,.mx-sm-9{margin-left:6rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:.75rem!important}.pt-sm-3,.py-sm-3{padding-top:.75rem!important}.pr-sm-3,.px-sm-3{padding-right:.75rem!important}.pb-sm-3,.py-sm-3{padding-bottom:.75rem!important}.pl-sm-3,.px-sm-3{padding-left:.75rem!important}.p-sm-4{padding:1rem!important}.pt-sm-4,.py-sm-4{padding-top:1rem!important}.pr-sm-4,.px-sm-4{padding-right:1rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1rem!important}.pl-sm-4,.px-sm-4{padding-left:1rem!important}.p-sm-5{padding:1.5rem!important}.pt-sm-5,.py-sm-5{padding-top:1.5rem!important}.pr-sm-5,.px-sm-5{padding-right:1.5rem!important}.pb-sm-5,.py-sm-5{padding-bottom:1.5rem!important}.pl-sm-5,.px-sm-5{padding-left:1.5rem!important}.p-sm-6{padding:2rem!important}.pt-sm-6,.py-sm-6{padding-top:2rem!important}.pr-sm-6,.px-sm-6{padding-right:2rem!important}.pb-sm-6,.py-sm-6{padding-bottom:2rem!important}.pl-sm-6,.px-sm-6{padding-left:2rem!important}.p-sm-7{padding:3rem!important}.pt-sm-7,.py-sm-7{padding-top:3rem!important}.pr-sm-7,.px-sm-7{padding-right:3rem!important}.pb-sm-7,.py-sm-7{padding-bottom:3rem!important}.pl-sm-7,.px-sm-7{padding-left:3rem!important}.p-sm-8{padding:4rem!important}.pt-sm-8,.py-sm-8{padding-top:4rem!important}.pr-sm-8,.px-sm-8{padding-right:4rem!important}.pb-sm-8,.py-sm-8{padding-bottom:4rem!important}.pl-sm-8,.px-sm-8{padding-left:4rem!important}.p-sm-9{padding:6rem!important}.pt-sm-9,.py-sm-9{padding-top:6rem!important}.pr-sm-9,.px-sm-9{padding-right:6rem!important}.pb-sm-9,.py-sm-9{padding-bottom:6rem!important}.pl-sm-9,.px-sm-9{padding-left:6rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:.75rem!important}.mt-md-3,.my-md-3{margin-top:.75rem!important}.mr-md-3,.mx-md-3{margin-right:.75rem!important}.mb-md-3,.my-md-3{margin-bottom:.75rem!important}.ml-md-3,.mx-md-3{margin-left:.75rem!important}.m-md-4{margin:1rem!important}.mt-md-4,.my-md-4{margin-top:1rem!important}.mr-md-4,.mx-md-4{margin-right:1rem!important}.mb-md-4,.my-md-4{margin-bottom:1rem!important}.ml-md-4,.mx-md-4{margin-left:1rem!important}.m-md-5{margin:1.5rem!important}.mt-md-5,.my-md-5{margin-top:1.5rem!important}.mr-md-5,.mx-md-5{margin-right:1.5rem!important}.mb-md-5,.my-md-5{margin-bottom:1.5rem!important}.ml-md-5,.mx-md-5{margin-left:1.5rem!important}.m-md-6{margin:2rem!important}.mt-md-6,.my-md-6{margin-top:2rem!important}.mr-md-6,.mx-md-6{margin-right:2rem!important}.mb-md-6,.my-md-6{margin-bottom:2rem!important}.ml-md-6,.mx-md-6{margin-left:2rem!important}.m-md-7{margin:3rem!important}.mt-md-7,.my-md-7{margin-top:3rem!important}.mr-md-7,.mx-md-7{margin-right:3rem!important}.mb-md-7,.my-md-7{margin-bottom:3rem!important}.ml-md-7,.mx-md-7{margin-left:3rem!important}.m-md-8{margin:4rem!important}.mt-md-8,.my-md-8{margin-top:4rem!important}.mr-md-8,.mx-md-8{margin-right:4rem!important}.mb-md-8,.my-md-8{margin-bottom:4rem!important}.ml-md-8,.mx-md-8{margin-left:4rem!important}.m-md-9{margin:6rem!important}.mt-md-9,.my-md-9{margin-top:6rem!important}.mr-md-9,.mx-md-9{margin-right:6rem!important}.mb-md-9,.my-md-9{margin-bottom:6rem!important}.ml-md-9,.mx-md-9{margin-left:6rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:.75rem!important}.pt-md-3,.py-md-3{padding-top:.75rem!important}.pr-md-3,.px-md-3{padding-right:.75rem!important}.pb-md-3,.py-md-3{padding-bottom:.75rem!important}.pl-md-3,.px-md-3{padding-left:.75rem!important}.p-md-4{padding:1rem!important}.pt-md-4,.py-md-4{padding-top:1rem!important}.pr-md-4,.px-md-4{padding-right:1rem!important}.pb-md-4,.py-md-4{padding-bottom:1rem!important}.pl-md-4,.px-md-4{padding-left:1rem!important}.p-md-5{padding:1.5rem!important}.pt-md-5,.py-md-5{padding-top:1.5rem!important}.pr-md-5,.px-md-5{padding-right:1.5rem!important}.pb-md-5,.py-md-5{padding-bottom:1.5rem!important}.pl-md-5,.px-md-5{padding-left:1.5rem!important}.p-md-6{padding:2rem!important}.pt-md-6,.py-md-6{padding-top:2rem!important}.pr-md-6,.px-md-6{padding-right:2rem!important}.pb-md-6,.py-md-6{padding-bottom:2rem!important}.pl-md-6,.px-md-6{padding-left:2rem!important}.p-md-7{padding:3rem!important}.pt-md-7,.py-md-7{padding-top:3rem!important}.pr-md-7,.px-md-7{padding-right:3rem!important}.pb-md-7,.py-md-7{padding-bottom:3rem!important}.pl-md-7,.px-md-7{padding-left:3rem!important}.p-md-8{padding:4rem!important}.pt-md-8,.py-md-8{padding-top:4rem!important}.pr-md-8,.px-md-8{padding-right:4rem!important}.pb-md-8,.py-md-8{padding-bottom:4rem!important}.pl-md-8,.px-md-8{padding-left:4rem!important}.p-md-9{padding:6rem!important}.pt-md-9,.py-md-9{padding-top:6rem!important}.pr-md-9,.px-md-9{padding-right:6rem!important}.pb-md-9,.py-md-9{padding-bottom:6rem!important}.pl-md-9,.px-md-9{padding-left:6rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:.75rem!important}.mt-lg-3,.my-lg-3{margin-top:.75rem!important}.mr-lg-3,.mx-lg-3{margin-right:.75rem!important}.mb-lg-3,.my-lg-3{margin-bottom:.75rem!important}.ml-lg-3,.mx-lg-3{margin-left:.75rem!important}.m-lg-4{margin:1rem!important}.mt-lg-4,.my-lg-4{margin-top:1rem!important}.mr-lg-4,.mx-lg-4{margin-right:1rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1rem!important}.ml-lg-4,.mx-lg-4{margin-left:1rem!important}.m-lg-5{margin:1.5rem!important}.mt-lg-5,.my-lg-5{margin-top:1.5rem!important}.mr-lg-5,.mx-lg-5{margin-right:1.5rem!important}.mb-lg-5,.my-lg-5{margin-bottom:1.5rem!important}.ml-lg-5,.mx-lg-5{margin-left:1.5rem!important}.m-lg-6{margin:2rem!important}.mt-lg-6,.my-lg-6{margin-top:2rem!important}.mr-lg-6,.mx-lg-6{margin-right:2rem!important}.mb-lg-6,.my-lg-6{margin-bottom:2rem!important}.ml-lg-6,.mx-lg-6{margin-left:2rem!important}.m-lg-7{margin:3rem!important}.mt-lg-7,.my-lg-7{margin-top:3rem!important}.mr-lg-7,.mx-lg-7{margin-right:3rem!important}.mb-lg-7,.my-lg-7{margin-bottom:3rem!important}.ml-lg-7,.mx-lg-7{margin-left:3rem!important}.m-lg-8{margin:4rem!important}.mt-lg-8,.my-lg-8{margin-top:4rem!important}.mr-lg-8,.mx-lg-8{margin-right:4rem!important}.mb-lg-8,.my-lg-8{margin-bottom:4rem!important}.ml-lg-8,.mx-lg-8{margin-left:4rem!important}.m-lg-9{margin:6rem!important}.mt-lg-9,.my-lg-9{margin-top:6rem!important}.mr-lg-9,.mx-lg-9{margin-right:6rem!important}.mb-lg-9,.my-lg-9{margin-bottom:6rem!important}.ml-lg-9,.mx-lg-9{margin-left:6rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:.75rem!important}.pt-lg-3,.py-lg-3{padding-top:.75rem!important}.pr-lg-3,.px-lg-3{padding-right:.75rem!important}.pb-lg-3,.py-lg-3{padding-bottom:.75rem!important}.pl-lg-3,.px-lg-3{padding-left:.75rem!important}.p-lg-4{padding:1rem!important}.pt-lg-4,.py-lg-4{padding-top:1rem!important}.pr-lg-4,.px-lg-4{padding-right:1rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1rem!important}.pl-lg-4,.px-lg-4{padding-left:1rem!important}.p-lg-5{padding:1.5rem!important}.pt-lg-5,.py-lg-5{padding-top:1.5rem!important}.pr-lg-5,.px-lg-5{padding-right:1.5rem!important}.pb-lg-5,.py-lg-5{padding-bottom:1.5rem!important}.pl-lg-5,.px-lg-5{padding-left:1.5rem!important}.p-lg-6{padding:2rem!important}.pt-lg-6,.py-lg-6{padding-top:2rem!important}.pr-lg-6,.px-lg-6{padding-right:2rem!important}.pb-lg-6,.py-lg-6{padding-bottom:2rem!important}.pl-lg-6,.px-lg-6{padding-left:2rem!important}.p-lg-7{padding:3rem!important}.pt-lg-7,.py-lg-7{padding-top:3rem!important}.pr-lg-7,.px-lg-7{padding-right:3rem!important}.pb-lg-7,.py-lg-7{padding-bottom:3rem!important}.pl-lg-7,.px-lg-7{padding-left:3rem!important}.p-lg-8{padding:4rem!important}.pt-lg-8,.py-lg-8{padding-top:4rem!important}.pr-lg-8,.px-lg-8{padding-right:4rem!important}.pb-lg-8,.py-lg-8{padding-bottom:4rem!important}.pl-lg-8,.px-lg-8{padding-left:4rem!important}.p-lg-9{padding:6rem!important}.pt-lg-9,.py-lg-9{padding-top:6rem!important}.pr-lg-9,.px-lg-9{padding-right:6rem!important}.pb-lg-9,.py-lg-9{padding-bottom:6rem!important}.pl-lg-9,.px-lg-9{padding-left:6rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1280px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:.75rem!important}.mt-xl-3,.my-xl-3{margin-top:.75rem!important}.mr-xl-3,.mx-xl-3{margin-right:.75rem!important}.mb-xl-3,.my-xl-3{margin-bottom:.75rem!important}.ml-xl-3,.mx-xl-3{margin-left:.75rem!important}.m-xl-4{margin:1rem!important}.mt-xl-4,.my-xl-4{margin-top:1rem!important}.mr-xl-4,.mx-xl-4{margin-right:1rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1rem!important}.ml-xl-4,.mx-xl-4{margin-left:1rem!important}.m-xl-5{margin:1.5rem!important}.mt-xl-5,.my-xl-5{margin-top:1.5rem!important}.mr-xl-5,.mx-xl-5{margin-right:1.5rem!important}.mb-xl-5,.my-xl-5{margin-bottom:1.5rem!important}.ml-xl-5,.mx-xl-5{margin-left:1.5rem!important}.m-xl-6{margin:2rem!important}.mt-xl-6,.my-xl-6{margin-top:2rem!important}.mr-xl-6,.mx-xl-6{margin-right:2rem!important}.mb-xl-6,.my-xl-6{margin-bottom:2rem!important}.ml-xl-6,.mx-xl-6{margin-left:2rem!important}.m-xl-7{margin:3rem!important}.mt-xl-7,.my-xl-7{margin-top:3rem!important}.mr-xl-7,.mx-xl-7{margin-right:3rem!important}.mb-xl-7,.my-xl-7{margin-bottom:3rem!important}.ml-xl-7,.mx-xl-7{margin-left:3rem!important}.m-xl-8{margin:4rem!important}.mt-xl-8,.my-xl-8{margin-top:4rem!important}.mr-xl-8,.mx-xl-8{margin-right:4rem!important}.mb-xl-8,.my-xl-8{margin-bottom:4rem!important}.ml-xl-8,.mx-xl-8{margin-left:4rem!important}.m-xl-9{margin:6rem!important}.mt-xl-9,.my-xl-9{margin-top:6rem!important}.mr-xl-9,.mx-xl-9{margin-right:6rem!important}.mb-xl-9,.my-xl-9{margin-bottom:6rem!important}.ml-xl-9,.mx-xl-9{margin-left:6rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:.75rem!important}.pt-xl-3,.py-xl-3{padding-top:.75rem!important}.pr-xl-3,.px-xl-3{padding-right:.75rem!important}.pb-xl-3,.py-xl-3{padding-bottom:.75rem!important}.pl-xl-3,.px-xl-3{padding-left:.75rem!important}.p-xl-4{padding:1rem!important}.pt-xl-4,.py-xl-4{padding-top:1rem!important}.pr-xl-4,.px-xl-4{padding-right:1rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1rem!important}.pl-xl-4,.px-xl-4{padding-left:1rem!important}.p-xl-5{padding:1.5rem!important}.pt-xl-5,.py-xl-5{padding-top:1.5rem!important}.pr-xl-5,.px-xl-5{padding-right:1.5rem!important}.pb-xl-5,.py-xl-5{padding-bottom:1.5rem!important}.pl-xl-5,.px-xl-5{padding-left:1.5rem!important}.p-xl-6{padding:2rem!important}.pt-xl-6,.py-xl-6{padding-top:2rem!important}.pr-xl-6,.px-xl-6{padding-right:2rem!important}.pb-xl-6,.py-xl-6{padding-bottom:2rem!important}.pl-xl-6,.px-xl-6{padding-left:2rem!important}.p-xl-7{padding:3rem!important}.pt-xl-7,.py-xl-7{padding-top:3rem!important}.pr-xl-7,.px-xl-7{padding-right:3rem!important}.pb-xl-7,.py-xl-7{padding-bottom:3rem!important}.pl-xl-7,.px-xl-7{padding-left:3rem!important}.p-xl-8{padding:4rem!important}.pt-xl-8,.py-xl-8{padding-top:4rem!important}.pr-xl-8,.px-xl-8{padding-right:4rem!important}.pb-xl-8,.py-xl-8{padding-bottom:4rem!important}.pl-xl-8,.px-xl-8{padding-left:4rem!important}.p-xl-9{padding:6rem!important}.pt-xl-9,.py-xl-9{padding-top:6rem!important}.pr-xl-9,.px-xl-9{padding-right:6rem!important}.pb-xl-9,.py-xl-9{padding-bottom:6rem!important}.pl-xl-9,.px-xl-9{padding-left:6rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.text-justify{text-align:justify!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media (min-width:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1280px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-italic{font-style:italic!important}.text-primary{color:#467fcf!important}a.text-primary:focus,a.text-primary:hover{color:#2f66b3!important}.text-secondary{color:#868e96!important}a.text-secondary:focus,a.text-secondary:hover{color:#6c757d!important}.text-success{color:#5eba00!important}a.text-success:focus,a.text-success:hover{color:#448700!important}.text-info{color:#45aaf2!important}a.text-info:focus,a.text-info:hover{color:#1594ef!important}.text-warning{color:#f1c40f!important}a.text-warning:focus,a.text-warning:hover{color:#c29d0b!important}.text-danger{color:#cd201f!important}a.text-danger:focus,a.text-danger:hover{color:#a11918!important}.text-light{color:#f8f9fa!important}a.text-light:focus,a.text-light:hover{color:#dae0e5!important}.text-dark{color:#343a40!important}a.text-dark:focus,a.text-dark:hover{color:#1d2124!important}.text-muted{color:#9aa0ac!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:initial;border:0}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,:after,:before{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]:after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}.container,body{min-width:992px!important}.navbar{display:none}.badge{border:1px solid #000}.table,.text-wrap table{border-collapse:collapse!important}.table td,.table th,.text-wrap table td,.text-wrap table th{background-color:#fff!important}.table-bordered td,.table-bordered th,.text-wrap table td,.text-wrap table th{border:1px solid #ddd!important}}html{font-size:16px}body,html{height:100%;direction:ltr}body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;-webkit-tap-highlight-color:transparent;-webkit-text-size-adjust:none;touch-action:manipulation;-webkit-font-feature-settings:"liga" 0;font-feature-settings:"liga" 0;overflow-y:scroll;position:relative}@media print{body{background:none}}body ::-webkit-scrollbar{width:6px;height:6px;transition:background .3s}body ::-webkit-scrollbar-thumb{background:#ced4da}body :hover::-webkit-scrollbar-thumb{background:#adb5bd}.lead{line-height:1.4}a{-webkit-text-decoration-skip:ink;text-decoration-skip:ink}.h1 a,.h2 a,.h3 a,.h4 a,.h5 a,.h6 a,h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{color:inherit}b,strong{font-weight:600}blockquote,ol,p,ul{margin-bottom:1em}blockquote{font-style:italic;color:#6e7687;padding-left:2rem;border-left:2px solid rgba(0,40,100,.12)}blockquote p{margin-bottom:1rem}blockquote cite{display:block;text-align:right}blockquote cite:before{content:"\2014 "}code{background:rgba(0,0,0,.025);border:1px solid rgba(0,0,0,.05);border-radius:3px;padding:3px}pre code{padding:0;border-radius:0;border:none;background:none}hr{margin-top:2rem;margin-bottom:2rem}pre{color:#343a40;padding:1rem;overflow:auto;font-size:85%;line-height:1.45;background-color:#f8fafc;border-radius:3px;-moz-tab-size:4;tab-size:4;text-shadow:0 1px #fff;-webkit-hyphens:none;-ms-hyphens:none;hyphens:none}img{max-width:100%}.text-wrap{font-size:1rem;line-height:1.66}.text-wrap>:first-child{margin-top:0}.text-wrap>:last-child{margin-bottom:0}.text-wrap>h1,.text-wrap>h2,.text-wrap>h3,.text-wrap>h4,.text-wrap>h5,.text-wrap>h6{margin-top:1em}.section-nav{background-color:#f8f9fa;margin:1rem 0;padding:.5rem 1rem;border:1px solid rgba(0,40,100,.12);border-radius:3px;list-style:none}.section-nav:before{content:"Table of contents:";display:block;font-weight:600}@media print{.container{max-width:none}}.row-cards>.col,.row-cards>[class*=col-]{display:flex;flex-direction:column}.row-deck>.col,.row-deck>[class*=col-]{display:flex;align-items:stretch}.row-deck>.col .card,.row-deck>[class*=col-] .card{flex:1 1 auto}.col-text{max-width:48rem}.col-login{max-width:24rem}.gutters-0{margin-right:0;margin-left:0}.gutters-0>.col,.gutters-0>[class*=col-]{padding-right:0;padding-left:0}.gutters-0 .card{margin-bottom:0}.gutters-xs{margin-right:-.25rem;margin-left:-.25rem}.gutters-xs>.col,.gutters-xs>[class*=col-]{padding-right:.25rem;padding-left:.25rem}.gutters-xs .card{margin-bottom:.5rem}.gutters-sm{margin-right:-.5rem;margin-left:-.5rem}.gutters-sm>.col,.gutters-sm>[class*=col-]{padding-right:.5rem;padding-left:.5rem}.gutters-sm .card{margin-bottom:1rem}.gutters-lg{margin-right:-1rem;margin-left:-1rem}.gutters-lg>.col,.gutters-lg>[class*=col-]{padding-right:1rem;padding-left:1rem}.gutters-lg .card{margin-bottom:2rem}.gutters-xl{margin-right:-1.5rem;margin-left:-1.5rem}.gutters-xl>.col,.gutters-xl>[class*=col-]{padding-right:1.5rem;padding-left:1.5rem}.gutters-xl .card{margin-bottom:3rem}.page{display:flex;flex-direction:column;justify-content:center;min-height:100%}body.fixed-header .page{padding-top:4.5rem}@media (min-width:1600px){body.aside-opened .page{margin-right:22rem}}.page-main{flex:1 1 auto}.page-content{margin:.75rem 0}@media (min-width:768px){.page-content{margin:1.5rem 0}}.page-header{display:flex;align-items:center;margin:1.5rem 0;flex-wrap:wrap}.page-title{margin:0;font-size:1.5rem;font-weight:400;line-height:2.5rem}.page-title-icon{color:#9aa0ac;font-size:1.25rem}.page-subtitle{font-size:.8125rem;color:#6e7687;margin-left:2rem}.page-subtitle a{color:inherit}.page-options{margin-left:auto}.page-breadcrumb{flex-basis:100%}.page-description{margin:.25rem 0 0;color:#6e7687}.page-description a{color:inherit}.page-single{flex:1 1 auto;display:flex;align-items:center;justify-content:center;padding:1rem 0}.content-heading{font-weight:400;margin:2rem 0 1.5rem;font-size:1.25rem;line-height:1.25}.content-heading:first-child{margin-top:0}.aside{position:fixed;top:0;right:0;bottom:0;width:22rem;background:#fff;border-left:1px solid rgba(0,40,100,.12);display:flex;flex-direction:column;z-index:100;visibility:hidden;box-shadow:0 0 5px 2px rgba(0,0,0,.05)}@media (min-width:1600px){body.aside-opened .aside{visibility:visible}}.aside-body{padding:1.5rem;flex:1 1 auto;overflow:auto}.aside-footer{padding:1rem 1.5rem;border-top:1px solid rgba(0,40,100,.12)}.aside-header{padding:1rem 1.5rem}.aside-header,.header{border-bottom:1px solid rgba(0,40,100,.12)}.header{padding-top:.75rem;padding-bottom:.75rem;background:#fff}body.fixed-header .header{position:fixed;top:0;left:0;right:0;z-index:1030}@media print{.header{display:none}}.header .dropdown-menu{margin-top:.75rem}.nav-unread{position:absolute;top:.25rem;right:.25rem;background:#cd201f;width:.5rem;height:.5rem;border-radius:50%}.header-brand{color:inherit;margin-right:1rem;font-size:1.25rem;white-space:nowrap;font-weight:600;padding:0;transition:opacity .3s;line-height:2rem}.header-brand:hover{opacity:.8;color:inherit;text-decoration:none}.header-brand-img{height:2rem;line-height:2rem;vertical-align:bottom;margin-right:.5rem;width:auto}.header-avatar{vertical-align:bottom;border-radius:50%}.header-avatar,.header-btn{width:2rem;height:2rem;display:inline-block}.header-btn{line-height:2rem;text-align:center;font-size:1rem}.header-btn.has-new{position:relative}.header-btn.has-new:before{content:"";width:6px;height:6px;background:#cd201f;position:absolute;top:4px;right:4px;border-radius:50%}.header-toggler{width:2rem;height:2rem;position:relative;color:#9aa0ac}.header-toggler:hover{color:#6e7687}.header-toggler-icon{position:absolute;width:1rem;height:2px;color:inherit;background:currentColor;border-radius:3px;top:50%;left:50%;margin:-2px 0 0 -.5rem;box-shadow:0 5px currentColor,0 -5px currentColor}.footer{background:#fff;border-top:1px solid rgba(0,40,100,.12);font-size:.875rem;padding:1.25rem 0;color:#9aa0ac}.footer a:not(.btn){color:#6e7687}@media print{.footer{display:none}}.bg-blue-lightest{background-color:#edf2fa!important}a.bg-blue-lightest:focus,a.bg-blue-lightest:hover,button.bg-blue-lightest:focus,button.bg-blue-lightest:hover{background-color:#c5d5ef!important}.bg-blue-lighter{background-color:#c8d9f1!important}a.bg-blue-lighter:focus,a.bg-blue-lighter:hover,button.bg-blue-lighter:focus,button.bg-blue-lighter:hover{background-color:#9fbde7!important}.bg-blue-light{background-color:#7ea5dd!important}a.bg-blue-light:focus,a.bg-blue-light:hover,button.bg-blue-light:focus,button.bg-blue-light:hover{background-color:#5689d2!important}.bg-blue-dark{background-color:#3866a6!important}a.bg-blue-dark:focus,a.bg-blue-dark:hover,button.bg-blue-dark:focus,button.bg-blue-dark:hover{background-color:#2b4f80!important}.bg-blue-darker{background-color:#1c3353!important}a.bg-blue-darker:focus,a.bg-blue-darker:hover,button.bg-blue-darker:focus,button.bg-blue-darker:hover{background-color:#0f1c2d!important}.bg-blue-darkest{background-color:#0e1929!important}a.bg-blue-darkest:focus,a.bg-blue-darkest:hover,button.bg-blue-darkest:focus,button.bg-blue-darkest:hover{background-color:#010203!important}.bg-indigo-lightest{background-color:#f0f1fa!important}a.bg-indigo-lightest:focus,a.bg-indigo-lightest:hover,button.bg-indigo-lightest:focus,button.bg-indigo-lightest:hover{background-color:#cacded!important}.bg-indigo-lighter{background-color:#d1d5f0!important}a.bg-indigo-lighter:focus,a.bg-indigo-lighter:hover,button.bg-indigo-lighter:focus,button.bg-indigo-lighter:hover{background-color:#abb2e3!important}.bg-indigo-light{background-color:#939edc!important}a.bg-indigo-light:focus,a.bg-indigo-light:hover,button.bg-indigo-light:focus,button.bg-indigo-light:hover{background-color:#6c7bd0!important}.bg-indigo-dark{background-color:#515da4!important}a.bg-indigo-dark:focus,a.bg-indigo-dark:hover,button.bg-indigo-dark:focus,button.bg-indigo-dark:hover{background-color:#404a82!important}.bg-indigo-darker{background-color:#282e52!important}a.bg-indigo-darker:focus,a.bg-indigo-darker:hover,button.bg-indigo-darker:focus,button.bg-indigo-darker:hover{background-color:#171b30!important}.bg-indigo-darkest{background-color:#141729!important}a.bg-indigo-darkest:focus,a.bg-indigo-darkest:hover,button.bg-indigo-darkest:focus,button.bg-indigo-darkest:hover{background-color:#030407!important}.bg-purple-lightest{background-color:#f6effd!important}a.bg-purple-lightest:focus,a.bg-purple-lightest:hover,button.bg-purple-lightest:focus,button.bg-purple-lightest:hover{background-color:#ddc2f7!important}.bg-purple-lighter{background-color:#e4cff9!important}a.bg-purple-lighter:focus,a.bg-purple-lighter:hover,button.bg-purple-lighter:focus,button.bg-purple-lighter:hover{background-color:#cba2f3!important}.bg-purple-light{background-color:#c08ef0!important}a.bg-purple-light:focus,a.bg-purple-light:hover,button.bg-purple-light:focus,button.bg-purple-light:hover{background-color:#a761ea!important}.bg-purple-dark{background-color:#844bbb!important}a.bg-purple-dark:focus,a.bg-purple-dark:hover,button.bg-purple-dark:focus,button.bg-purple-dark:hover{background-color:#6a3a99!important}.bg-purple-darker{background-color:#42265e!important}a.bg-purple-darker:focus,a.bg-purple-darker:hover,button.bg-purple-darker:focus,button.bg-purple-darker:hover{background-color:#29173a!important}.bg-purple-darkest{background-color:#21132f!important}a.bg-purple-darkest:focus,a.bg-purple-darkest:hover,button.bg-purple-darkest:focus,button.bg-purple-darkest:hover{background-color:#08040b!important}.bg-pink-lightest{background-color:#fef0f5!important}a.bg-pink-lightest:focus,a.bg-pink-lightest:hover,button.bg-pink-lightest:focus,button.bg-pink-lightest:hover{background-color:#fbc0d5!important}.bg-pink-lighter{background-color:#fcd3e1!important}a.bg-pink-lighter:focus,a.bg-pink-lighter:hover,button.bg-pink-lighter:focus,button.bg-pink-lighter:hover{background-color:#f9a3c0!important}.bg-pink-light{background-color:#f999b9!important}a.bg-pink-light:focus,a.bg-pink-light:hover,button.bg-pink-light:focus,button.bg-pink-light:hover{background-color:#f66998!important}.bg-pink-dark{background-color:#c5577c!important}a.bg-pink-dark:focus,a.bg-pink-dark:hover,button.bg-pink-dark:focus,button.bg-pink-dark:hover{background-color:#ad3c62!important}.bg-pink-darker{background-color:#622c3e!important}a.bg-pink-darker:focus,a.bg-pink-darker:hover,button.bg-pink-darker:focus,button.bg-pink-darker:hover{background-color:#3f1c28!important}.bg-pink-darkest{background-color:#31161f!important}a.bg-pink-darkest:focus,a.bg-pink-darkest:hover,button.bg-pink-darkest:focus,button.bg-pink-darkest:hover{background-color:#0e0609!important}.bg-red-lightest{background-color:#fae9e9!important}a.bg-red-lightest:focus,a.bg-red-lightest:hover,button.bg-red-lightest:focus,button.bg-red-lightest:hover{background-color:#f1bfbf!important}.bg-red-lighter{background-color:#f0bcbc!important}a.bg-red-lighter:focus,a.bg-red-lighter:hover,button.bg-red-lighter:focus,button.bg-red-lighter:hover{background-color:#e79292!important}.bg-red-light{background-color:#dc6362!important}a.bg-red-light:focus,a.bg-red-light:hover,button.bg-red-light:focus,button.bg-red-light:hover{background-color:#d33a38!important}.bg-red-dark{background-color:#a41a19!important}a.bg-red-dark:focus,a.bg-red-dark:hover,button.bg-red-dark:focus,button.bg-red-dark:hover{background-color:#781312!important}.bg-red-darker{background-color:#520d0c!important}a.bg-red-darker:focus,a.bg-red-darker:hover,button.bg-red-darker:focus,button.bg-red-darker:hover{background-color:#260605!important}.bg-red-darkest{background-color:#290606!important}a.bg-red-darkest:focus,a.bg-red-darkest:hover,button.bg-red-darkest:focus,button.bg-red-darkest:hover{background-color:#000!important}.bg-orange-lightest{background-color:#fff5ec!important}a.bg-orange-lightest:focus,a.bg-orange-lightest:hover,button.bg-orange-lightest:focus,button.bg-orange-lightest:hover{background-color:#ffdab9!important}.bg-orange-lighter{background-color:#fee0c7!important}a.bg-orange-lighter:focus,a.bg-orange-lighter:hover,button.bg-orange-lighter:focus,button.bg-orange-lighter:hover{background-color:#fdc495!important}.bg-orange-light{background-color:#feb67c!important}a.bg-orange-light:focus,a.bg-orange-light:hover,button.bg-orange-light:focus,button.bg-orange-light:hover{background-color:#fe9a49!important}.bg-orange-dark{background-color:#ca7836!important}a.bg-orange-dark:focus,a.bg-orange-dark:hover,button.bg-orange-dark:focus,button.bg-orange-dark:hover{background-color:#a2602b!important}.bg-orange-darker{background-color:#653c1b!important}a.bg-orange-darker:focus,a.bg-orange-darker:hover,button.bg-orange-darker:focus,button.bg-orange-darker:hover{background-color:#3d2410!important}.bg-orange-darkest{background-color:#331e0e!important}a.bg-orange-darkest:focus,a.bg-orange-darkest:hover,button.bg-orange-darkest:focus,button.bg-orange-darkest:hover{background-color:#0b0603!important}.bg-yellow-lightest{background-color:#fef9e7!important}a.bg-yellow-lightest:focus,a.bg-yellow-lightest:hover,button.bg-yellow-lightest:focus,button.bg-yellow-lightest:hover{background-color:#fcedb6!important}.bg-yellow-lighter{background-color:#fbedb7!important}a.bg-yellow-lighter:focus,a.bg-yellow-lighter:hover,button.bg-yellow-lighter:focus,button.bg-yellow-lighter:hover{background-color:#f8e187!important}.bg-yellow-light{background-color:#f5d657!important}a.bg-yellow-light:focus,a.bg-yellow-light:hover,button.bg-yellow-light:focus,button.bg-yellow-light:hover{background-color:#f2ca27!important}.bg-yellow-dark{background-color:#c19d0c!important}a.bg-yellow-dark:focus,a.bg-yellow-dark:hover,button.bg-yellow-dark:focus,button.bg-yellow-dark:hover{background-color:#917609!important}.bg-yellow-darker{background-color:#604e06!important}.bg-yellow-darkest,a.bg-yellow-darker:focus,a.bg-yellow-darker:hover,button.bg-yellow-darker:focus,button.bg-yellow-darker:hover{background-color:#302703!important}a.bg-yellow-darkest:focus,a.bg-yellow-darkest:hover,button.bg-yellow-darkest:focus,button.bg-yellow-darkest:hover{background-color:#000!important}.bg-green-lightest{background-color:#eff8e6!important}a.bg-green-lightest:focus,a.bg-green-lightest:hover,button.bg-green-lightest:focus,button.bg-green-lightest:hover{background-color:#d6edbe!important}.bg-green-lighter{background-color:#cfeab3!important}a.bg-green-lighter:focus,a.bg-green-lighter:hover,button.bg-green-lighter:focus,button.bg-green-lighter:hover{background-color:#b6df8b!important}.bg-green-light{background-color:#8ecf4d!important}a.bg-green-light:focus,a.bg-green-light:hover,button.bg-green-light:focus,button.bg-green-light:hover{background-color:#75b831!important}.bg-green-dark{background-color:#4b9500!important}a.bg-green-dark:focus,a.bg-green-dark:hover,button.bg-green-dark:focus,button.bg-green-dark:hover{background-color:#316200!important}.bg-green-darker{background-color:#264a00!important}a.bg-green-darker:focus,a.bg-green-darker:hover,button.bg-green-darker:focus,button.bg-green-darker:hover{background-color:#0c1700!important}.bg-green-darkest{background-color:#132500!important}a.bg-green-darkest:focus,a.bg-green-darkest:hover,button.bg-green-darkest:focus,button.bg-green-darkest:hover{background-color:#000!important}.bg-teal-lightest{background-color:#eafaf8!important}a.bg-teal-lightest:focus,a.bg-teal-lightest:hover,button.bg-teal-lightest:focus,button.bg-teal-lightest:hover{background-color:#c1f0ea!important}.bg-teal-lighter{background-color:#bfefea!important}a.bg-teal-lighter:focus,a.bg-teal-lighter:hover,button.bg-teal-lighter:focus,button.bg-teal-lighter:hover{background-color:#96e5dd!important}.bg-teal-light{background-color:#6bdbcf!important}a.bg-teal-light:focus,a.bg-teal-light:hover,button.bg-teal-light:focus,button.bg-teal-light:hover{background-color:#42d1c2!important}.bg-teal-dark{background-color:#22a295!important}a.bg-teal-dark:focus,a.bg-teal-dark:hover,button.bg-teal-dark:focus,button.bg-teal-dark:hover{background-color:#19786e!important}.bg-teal-darker{background-color:#11514a!important}a.bg-teal-darker:focus,a.bg-teal-darker:hover,button.bg-teal-darker:focus,button.bg-teal-darker:hover{background-color:#082723!important}.bg-teal-darkest{background-color:#092925!important}a.bg-teal-darkest:focus,a.bg-teal-darkest:hover,button.bg-teal-darkest:focus,button.bg-teal-darkest:hover{background-color:#000!important}.bg-cyan-lightest{background-color:#e8f6f8!important}a.bg-cyan-lightest:focus,a.bg-cyan-lightest:hover,button.bg-cyan-lightest:focus,button.bg-cyan-lightest:hover{background-color:#c1e7ec!important}.bg-cyan-lighter{background-color:#b9e3ea!important}a.bg-cyan-lighter:focus,a.bg-cyan-lighter:hover,button.bg-cyan-lighter:focus,button.bg-cyan-lighter:hover{background-color:#92d3de!important}.bg-cyan-light{background-color:#5dbecd!important}a.bg-cyan-light:focus,a.bg-cyan-light:hover,button.bg-cyan-light:focus,button.bg-cyan-light:hover{background-color:#3aabbd!important}.bg-cyan-dark{background-color:#128293!important}a.bg-cyan-dark:focus,a.bg-cyan-dark:hover,button.bg-cyan-dark:focus,button.bg-cyan-dark:hover{background-color:#0c5a66!important}.bg-cyan-darker{background-color:#09414a!important}a.bg-cyan-darker:focus,a.bg-cyan-darker:hover,button.bg-cyan-darker:focus,button.bg-cyan-darker:hover{background-color:#03191d!important}.bg-cyan-darkest{background-color:#052025!important}a.bg-cyan-darkest:focus,a.bg-cyan-darkest:hover,button.bg-cyan-darkest:focus,button.bg-cyan-darkest:hover{background-color:#000!important}.bg-white-lightest{background-color:#fff!important}a.bg-white-lightest:focus,a.bg-white-lightest:hover,button.bg-white-lightest:focus,button.bg-white-lightest:hover{background-color:#e6e5e5!important}.bg-white-lighter{background-color:#fff!important}a.bg-white-lighter:focus,a.bg-white-lighter:hover,button.bg-white-lighter:focus,button.bg-white-lighter:hover{background-color:#e6e5e5!important}.bg-white-light{background-color:#fff!important}a.bg-white-light:focus,a.bg-white-light:hover,button.bg-white-light:focus,button.bg-white-light:hover{background-color:#e6e5e5!important}.bg-white-dark{background-color:#ccc!important}a.bg-white-dark:focus,a.bg-white-dark:hover,button.bg-white-dark:focus,button.bg-white-dark:hover{background-color:#b3b2b2!important}.bg-white-darker{background-color:#666!important}a.bg-white-darker:focus,a.bg-white-darker:hover,button.bg-white-darker:focus,button.bg-white-darker:hover{background-color:#4d4c4c!important}.bg-white-darkest{background-color:#333!important}a.bg-white-darkest:focus,a.bg-white-darkest:hover,button.bg-white-darkest:focus,button.bg-white-darkest:hover{background-color:#1a1919!important}.bg-gray-lightest{background-color:#f3f4f5!important}a.bg-gray-lightest:focus,a.bg-gray-lightest:hover,button.bg-gray-lightest:focus,button.bg-gray-lightest:hover{background-color:#d7dbde!important}.bg-gray-lighter{background-color:#dbdde0!important}a.bg-gray-lighter:focus,a.bg-gray-lighter:hover,button.bg-gray-lighter:focus,button.bg-gray-lighter:hover{background-color:#c0c3c8!important}.bg-gray-light{background-color:#aab0b6!important}a.bg-gray-light:focus,a.bg-gray-light:hover,button.bg-gray-light:focus,button.bg-gray-light:hover{background-color:#8f979e!important}.bg-gray-dark{background-color:#6b7278!important}a.bg-gray-dark:focus,a.bg-gray-dark:hover,button.bg-gray-dark:focus,button.bg-gray-dark:hover{background-color:#53585d!important}.bg-gray-darker{background-color:#36393c!important}a.bg-gray-darker:focus,a.bg-gray-darker:hover,button.bg-gray-darker:focus,button.bg-gray-darker:hover{background-color:#1e2021!important}.bg-gray-darkest{background-color:#1b1c1e!important}a.bg-gray-darkest:focus,a.bg-gray-darkest:hover,button.bg-gray-darkest:focus,button.bg-gray-darkest:hover{background-color:#030303!important}.bg-gray-dark-lightest{background-color:#ebebec!important}a.bg-gray-dark-lightest:focus,a.bg-gray-dark-lightest:hover,button.bg-gray-dark-lightest:focus,button.bg-gray-dark-lightest:hover{background-color:#d1d1d3!important}.bg-gray-dark-lighter{background-color:#c2c4c6!important}a.bg-gray-dark-lighter:focus,a.bg-gray-dark-lighter:hover,button.bg-gray-dark-lighter:focus,button.bg-gray-dark-lighter:hover{background-color:#a8abad!important}.bg-gray-dark-light{background-color:#717579!important}a.bg-gray-dark-light:focus,a.bg-gray-dark-light:hover,button.bg-gray-dark-light:focus,button.bg-gray-dark-light:hover{background-color:#585c5f!important}.bg-gray-dark-dark{background-color:#2a2e33!important}a.bg-gray-dark-dark:focus,a.bg-gray-dark-dark:hover,button.bg-gray-dark-dark:focus,button.bg-gray-dark-dark:hover{background-color:#131517!important}.bg-gray-dark-darker{background-color:#15171a!important}a.bg-gray-dark-darker:focus,a.bg-gray-dark-darker:hover,button.bg-gray-dark-darker:focus,button.bg-gray-dark-darker:hover{background-color:#000!important}.bg-gray-dark-darkest{background-color:#0a0c0d!important}a.bg-gray-dark-darkest:focus,a.bg-gray-dark-darkest:hover,button.bg-gray-dark-darkest:focus,button.bg-gray-dark-darkest:hover{background-color:#000!important}.bg-azure-lightest{background-color:#ecf7fe!important}a.bg-azure-lightest:focus,a.bg-azure-lightest:hover,button.bg-azure-lightest:focus,button.bg-azure-lightest:hover{background-color:#bce3fb!important}.bg-azure-lighter{background-color:#c7e6fb!important}a.bg-azure-lighter:focus,a.bg-azure-lighter:hover,button.bg-azure-lighter:focus,button.bg-azure-lighter:hover{background-color:#97d1f8!important}.bg-azure-light{background-color:#7dc4f6!important}a.bg-azure-light:focus,a.bg-azure-light:hover,button.bg-azure-light:focus,button.bg-azure-light:hover{background-color:#4daef3!important}.bg-azure-dark{background-color:#3788c2!important}a.bg-azure-dark:focus,a.bg-azure-dark:hover,button.bg-azure-dark:focus,button.bg-azure-dark:hover{background-color:#2c6c9a!important}.bg-azure-darker{background-color:#1c4461!important}a.bg-azure-darker:focus,a.bg-azure-darker:hover,button.bg-azure-darker:focus,button.bg-azure-darker:hover{background-color:#112839!important}.bg-azure-darkest{background-color:#0e2230!important}a.bg-azure-darkest:focus,a.bg-azure-darkest:hover,button.bg-azure-darkest:focus,button.bg-azure-darkest:hover{background-color:#020609!important}.bg-lime-lightest{background-color:#f2fbeb!important}a.bg-lime-lightest:focus,a.bg-lime-lightest:hover,button.bg-lime-lightest:focus,button.bg-lime-lightest:hover{background-color:#d6f3c1!important}.bg-lime-lighter{background-color:#d7f2c2!important}a.bg-lime-lighter:focus,a.bg-lime-lighter:hover,button.bg-lime-lighter:focus,button.bg-lime-lighter:hover{background-color:#bbe998!important}.bg-lime-light{background-color:#a3e072!important}a.bg-lime-light:focus,a.bg-lime-light:hover,button.bg-lime-light:focus,button.bg-lime-light:hover{background-color:#88d748!important}.bg-lime-dark{background-color:#62a82a!important}a.bg-lime-dark:focus,a.bg-lime-dark:hover,button.bg-lime-dark:focus,button.bg-lime-dark:hover{background-color:#4a7f20!important}.bg-lime-darker{background-color:#315415!important}a.bg-lime-darker:focus,a.bg-lime-darker:hover,button.bg-lime-darker:focus,button.bg-lime-darker:hover{background-color:#192b0b!important}.bg-lime-darkest{background-color:#192a0b!important}a.bg-lime-darkest:focus,a.bg-lime-darkest:hover,button.bg-lime-darkest:focus,button.bg-lime-darkest:hover{background-color:#010200!important}.display-1 i,.display-2 i,.display-3 i,.display-4 i{vertical-align:initial;font-size:.815em}.text-inherit{color:inherit!important}.text-default{color:#495057!important}.text-muted-dark{color:#6e7687!important}.tracking-tight{letter-spacing:-.05em!important}.tracking-normal{letter-spacing:0!important}.tracking-wide{letter-spacing:.05em!important}.leading-none{line-height:1!important}.leading-tight{line-height:1.25!important}.leading-normal{line-height:1.5!important}.leading-loose{line-height:2!important}.bg-blue{background-color:#467fcf!important}a.bg-blue:focus,a.bg-blue:hover,button.bg-blue:focus,button.bg-blue:hover{background-color:#2f66b3!important}.text-blue{color:#467fcf!important}.bg-indigo{background-color:#6574cd!important}a.bg-indigo:focus,a.bg-indigo:hover,button.bg-indigo:focus,button.bg-indigo:hover{background-color:#3f51c1!important}.text-indigo{color:#6574cd!important}.bg-purple{background-color:#a55eea!important}a.bg-purple:focus,a.bg-purple:hover,button.bg-purple:focus,button.bg-purple:hover{background-color:#8c31e4!important}.text-purple{color:#a55eea!important}.bg-pink{background-color:#f66d9b!important}a.bg-pink:focus,a.bg-pink:hover,button.bg-pink:focus,button.bg-pink:hover{background-color:#f33d7a!important}.text-pink{color:#f66d9b!important}.bg-red{background-color:#cd201f!important}a.bg-red:focus,a.bg-red:hover,button.bg-red:focus,button.bg-red:hover{background-color:#a11918!important}.text-red{color:#cd201f!important}.bg-orange{background-color:#fd9644!important}a.bg-orange:focus,a.bg-orange:hover,button.bg-orange:focus,button.bg-orange:hover{background-color:#fc7a12!important}.text-orange{color:#fd9644!important}.bg-yellow{background-color:#f1c40f!important}a.bg-yellow:focus,a.bg-yellow:hover,button.bg-yellow:focus,button.bg-yellow:hover{background-color:#c29d0b!important}.text-yellow{color:#f1c40f!important}.bg-green{background-color:#5eba00!important}a.bg-green:focus,a.bg-green:hover,button.bg-green:focus,button.bg-green:hover{background-color:#448700!important}.text-green{color:#5eba00!important}.bg-teal{background-color:#2bcbba!important}a.bg-teal:focus,a.bg-teal:hover,button.bg-teal:focus,button.bg-teal:hover{background-color:#22a193!important}.text-teal{color:#2bcbba!important}.bg-cyan{background-color:#17a2b8!important}a.bg-cyan:focus,a.bg-cyan:hover,button.bg-cyan:focus,button.bg-cyan:hover{background-color:#117a8b!important}.text-cyan{color:#17a2b8!important}.bg-white{background-color:#fff!important}a.bg-white:focus,a.bg-white:hover,button.bg-white:focus,button.bg-white:hover{background-color:#e6e5e5!important}.text-white{color:#fff!important}.bg-gray{background-color:#868e96!important}a.bg-gray:focus,a.bg-gray:hover,button.bg-gray:focus,button.bg-gray:hover{background-color:#6c757d!important}.text-gray{color:#868e96!important}.bg-gray-dark{background-color:#343a40!important}a.bg-gray-dark:focus,a.bg-gray-dark:hover,button.bg-gray-dark:focus,button.bg-gray-dark:hover{background-color:#1d2124!important}.text-gray-dark{color:#343a40!important}.bg-azure{background-color:#45aaf2!important}a.bg-azure:focus,a.bg-azure:hover,button.bg-azure:focus,button.bg-azure:hover{background-color:#1594ef!important}.text-azure{color:#45aaf2!important}.bg-lime{background-color:#7bd235!important}a.bg-lime:focus,a.bg-lime:hover,button.bg-lime:focus,button.bg-lime:hover{background-color:#63ad27!important}.text-lime{color:#7bd235!important}.icon{color:#9aa0ac!important}.icon i{vertical-align:-1px}a.icon{text-decoration:none;cursor:pointer}a.icon:hover{color:#495057!important}.o-auto{overflow:auto!important}.o-hidden{overflow:hidden!important}.shadow{box-shadow:0 1px 2px 0 rgba(0,0,0,.05)!important}.shadow-none{box-shadow:none!important}.nav-item,.nav-link{padding:0 .75rem;min-width:2rem;transition:color .3s;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:pointer;display:flex;align-items:center}.nav-item .badge,.nav-link .badge{position:absolute;top:0;right:0;padding:.2rem .25rem;min-width:1rem}.nav-tabs{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;color:#9aa0ac;margin:0 -.75rem}.nav-tabs .nav-link{border:0;color:inherit;border-bottom:1px solid transparent;margin-bottom:-1px;transition:border-color .3s;font-weight:400;padding:1rem 0}.nav-tabs .nav-link:hover:not(.disabled){border-color:#6e7687;color:#6e7687}.nav-tabs .nav-link.active{border-color:#467fcf;color:#467fcf;background:transparent}.nav-tabs .nav-link.disabled{opacity:.4;cursor:default;pointer-events:none}.nav-tabs .nav-item{margin-bottom:0;position:relative}.nav-tabs .nav-item i{margin-right:.25rem;line-height:1;font-size:.875rem;width:.875rem;vertical-align:initial;display:inline-block}.nav-tabs .nav-item:hover .nav-submenu{display:block}.nav-tabs .nav-submenu{display:none;position:absolute;background:#fff;border:1px solid rgba(0,40,100,.12);border-top:none;z-index:10;box-shadow:0 1px 2px 0 rgba(0,0,0,.05);min-width:10rem;border-radius:0 0 3px 3px}.nav-tabs .nav-submenu .nav-item{display:block;padding:.5rem 1rem;color:#9aa0ac;margin:0!important;cursor:pointer;transition:background .3s}.nav-tabs .nav-submenu .nav-item.active{color:#467fcf}.nav-tabs .nav-submenu .nav-item:hover{color:#6e7687;text-decoration:none;background:rgba(0,0,0,.024)}.btn{cursor:pointer;font-weight:600;letter-spacing:.03em;font-size:.8125rem;min-width:2.375rem}.btn i{font-size:1rem;vertical-align:-2px}.btn-icon{padding-left:.5rem;padding-right:.5rem;text-align:center}.btn-secondary{color:#495057;background-color:#fff;border-color:rgba(0,40,100,.12);box-shadow:0 1px 1px 0 rgba(0,0,0,.05)}.btn-secondary:hover{color:#495057;background-color:#f6f6f6;border-color:rgba(0,20,49,.12)}.btn-secondary.focus,.btn-secondary:focus{box-shadow:0 0 0 2px rgba(0,40,100,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#495057;background-color:#fff;border-color:rgba(0,40,100,.12)}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#495057;background-color:#e6e5e5;border-color:rgba(0,15,36,.12)}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(0,40,100,.5)}.btn-pill{border-radius:10rem;padding-left:1.5em;padding-right:1.5em}.btn-square{border-radius:0}.btn-facebook{color:#fff;background-color:#3b5998;border-color:#3b5998}.btn-facebook:hover{color:#fff;background-color:#30497c;border-color:#2d4373}.btn-facebook.focus,.btn-facebook:focus{box-shadow:0 0 0 2px rgba(59,89,152,.5)}.btn-facebook.disabled,.btn-facebook:disabled{color:#fff;background-color:#3b5998;border-color:#3b5998}.btn-facebook:not(:disabled):not(.disabled).active,.btn-facebook:not(:disabled):not(.disabled):active,.show>.btn-facebook.dropdown-toggle{color:#fff;background-color:#2d4373;border-color:#293e6a}.btn-facebook:not(:disabled):not(.disabled).active:focus,.btn-facebook:not(:disabled):not(.disabled):active:focus,.show>.btn-facebook.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(59,89,152,.5)}.btn-twitter{color:#fff;background-color:#1da1f2;border-color:#1da1f2}.btn-twitter:hover{color:#fff;background-color:#0d8ddc;border-color:#0c85d0}.btn-twitter.focus,.btn-twitter:focus{box-shadow:0 0 0 2px rgba(29,161,242,.5)}.btn-twitter.disabled,.btn-twitter:disabled{color:#fff;background-color:#1da1f2;border-color:#1da1f2}.btn-twitter:not(:disabled):not(.disabled).active,.btn-twitter:not(:disabled):not(.disabled):active,.show>.btn-twitter.dropdown-toggle{color:#fff;background-color:#0c85d0;border-color:#0b7ec4}.btn-twitter:not(:disabled):not(.disabled).active:focus,.btn-twitter:not(:disabled):not(.disabled):active:focus,.show>.btn-twitter.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(29,161,242,.5)}.btn-google{color:#fff;background-color:#dc4e41;border-color:#dc4e41}.btn-google:hover{color:#fff;background-color:#d03526;border-color:#c63224}.btn-google.focus,.btn-google:focus{box-shadow:0 0 0 2px rgba(220,78,65,.5)}.btn-google.disabled,.btn-google:disabled{color:#fff;background-color:#dc4e41;border-color:#dc4e41}.btn-google:not(:disabled):not(.disabled).active,.btn-google:not(:disabled):not(.disabled):active,.show>.btn-google.dropdown-toggle{color:#fff;background-color:#c63224;border-color:#bb2f22}.btn-google:not(:disabled):not(.disabled).active:focus,.btn-google:not(:disabled):not(.disabled):active:focus,.show>.btn-google.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(220,78,65,.5)}.btn-youtube{color:#fff;background-color:red;border-color:red}.btn-youtube:hover{color:#fff;background-color:#d90000;border-color:#c00}.btn-youtube.focus,.btn-youtube:focus{box-shadow:0 0 0 2px rgba(255,0,0,.5)}.btn-youtube.disabled,.btn-youtube:disabled{color:#fff;background-color:red;border-color:red}.btn-youtube:not(:disabled):not(.disabled).active,.btn-youtube:not(:disabled):not(.disabled):active,.show>.btn-youtube.dropdown-toggle{color:#fff;background-color:#c00;border-color:#bf0000}.btn-youtube:not(:disabled):not(.disabled).active:focus,.btn-youtube:not(:disabled):not(.disabled):active:focus,.show>.btn-youtube.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(255,0,0,.5)}.btn-vimeo{color:#fff;background-color:#1ab7ea;border-color:#1ab7ea}.btn-vimeo:hover{color:#fff;background-color:#139ecb;border-color:#1295bf}.btn-vimeo.focus,.btn-vimeo:focus{box-shadow:0 0 0 2px rgba(26,183,234,.5)}.btn-vimeo.disabled,.btn-vimeo:disabled{color:#fff;background-color:#1ab7ea;border-color:#1ab7ea}.btn-vimeo:not(:disabled):not(.disabled).active,.btn-vimeo:not(:disabled):not(.disabled):active,.show>.btn-vimeo.dropdown-toggle{color:#fff;background-color:#1295bf;border-color:#108cb4}.btn-vimeo:not(:disabled):not(.disabled).active:focus,.btn-vimeo:not(:disabled):not(.disabled):active:focus,.show>.btn-vimeo.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(26,183,234,.5)}.btn-dribbble{color:#fff;background-color:#ea4c89;border-color:#ea4c89}.btn-dribbble:hover{color:#fff;background-color:#e62a72;border-color:#e51e6b}.btn-dribbble.focus,.btn-dribbble:focus{box-shadow:0 0 0 2px rgba(234,76,137,.5)}.btn-dribbble.disabled,.btn-dribbble:disabled{color:#fff;background-color:#ea4c89;border-color:#ea4c89}.btn-dribbble:not(:disabled):not(.disabled).active,.btn-dribbble:not(:disabled):not(.disabled):active,.show>.btn-dribbble.dropdown-toggle{color:#fff;background-color:#e51e6b;border-color:#dc1a65}.btn-dribbble:not(:disabled):not(.disabled).active:focus,.btn-dribbble:not(:disabled):not(.disabled):active:focus,.show>.btn-dribbble.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(234,76,137,.5)}.btn-github{color:#fff;background-color:#181717;border-color:#181717}.btn-github:hover{color:#fff;background-color:#040404;border-color:#000}.btn-github.focus,.btn-github:focus{box-shadow:0 0 0 2px rgba(24,23,23,.5)}.btn-github.disabled,.btn-github:disabled{color:#fff;background-color:#181717;border-color:#181717}.btn-github:not(:disabled):not(.disabled).active,.btn-github:not(:disabled):not(.disabled):active,.show>.btn-github.dropdown-toggle{color:#fff;background-color:#000;border-color:#000}.btn-github:not(:disabled):not(.disabled).active:focus,.btn-github:not(:disabled):not(.disabled):active:focus,.show>.btn-github.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(24,23,23,.5)}.btn-instagram{color:#fff;background-color:#e4405f;border-color:#e4405f}.btn-instagram:hover{color:#fff;background-color:#de1f44;border-color:#d31e40}.btn-instagram.focus,.btn-instagram:focus{box-shadow:0 0 0 2px rgba(228,64,95,.5)}.btn-instagram.disabled,.btn-instagram:disabled{color:#fff;background-color:#e4405f;border-color:#e4405f}.btn-instagram:not(:disabled):not(.disabled).active,.btn-instagram:not(:disabled):not(.disabled):active,.show>.btn-instagram.dropdown-toggle{color:#fff;background-color:#d31e40;border-color:#c81c3d}.btn-instagram:not(:disabled):not(.disabled).active:focus,.btn-instagram:not(:disabled):not(.disabled):active:focus,.show>.btn-instagram.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(228,64,95,.5)}.btn-pinterest{color:#fff;background-color:#bd081c;border-color:#bd081c}.btn-pinterest:hover{color:#fff;background-color:#980617;border-color:#8c0615}.btn-pinterest.focus,.btn-pinterest:focus{box-shadow:0 0 0 2px rgba(189,8,28,.5)}.btn-pinterest.disabled,.btn-pinterest:disabled{color:#fff;background-color:#bd081c;border-color:#bd081c}.btn-pinterest:not(:disabled):not(.disabled).active,.btn-pinterest:not(:disabled):not(.disabled):active,.show>.btn-pinterest.dropdown-toggle{color:#fff;background-color:#8c0615;border-color:#800513}.btn-pinterest:not(:disabled):not(.disabled).active:focus,.btn-pinterest:not(:disabled):not(.disabled):active:focus,.show>.btn-pinterest.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(189,8,28,.5)}.btn-vk{color:#fff;background-color:#6383a8;border-color:#6383a8}.btn-vk:hover{color:#fff;background-color:#527093;border-color:#4d6a8b}.btn-vk.focus,.btn-vk:focus{box-shadow:0 0 0 2px rgba(99,131,168,.5)}.btn-vk.disabled,.btn-vk:disabled{color:#fff;background-color:#6383a8;border-color:#6383a8}.btn-vk:not(:disabled):not(.disabled).active,.btn-vk:not(:disabled):not(.disabled):active,.show>.btn-vk.dropdown-toggle{color:#fff;background-color:#4d6a8b;border-color:#496482}.btn-vk:not(:disabled):not(.disabled).active:focus,.btn-vk:not(:disabled):not(.disabled):active:focus,.show>.btn-vk.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(99,131,168,.5)}.btn-rss{color:#fff;background-color:orange;border-color:orange}.btn-rss:hover{color:#fff;background-color:#d98c00;border-color:#cc8400}.btn-rss.focus,.btn-rss:focus{box-shadow:0 0 0 2px rgba(255,165,0,.5)}.btn-rss.disabled,.btn-rss:disabled{color:#fff;background-color:orange;border-color:orange}.btn-rss:not(:disabled):not(.disabled).active,.btn-rss:not(:disabled):not(.disabled):active,.show>.btn-rss.dropdown-toggle{color:#fff;background-color:#cc8400;border-color:#bf7c00}.btn-rss:not(:disabled):not(.disabled).active:focus,.btn-rss:not(:disabled):not(.disabled):active:focus,.show>.btn-rss.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(255,165,0,.5)}.btn-flickr{color:#fff;background-color:#0063dc;border-color:#0063dc}.btn-flickr:hover{color:#fff;background-color:#0052b6;border-color:#004ca9}.btn-flickr.focus,.btn-flickr:focus{box-shadow:0 0 0 2px rgba(0,99,220,.5)}.btn-flickr.disabled,.btn-flickr:disabled{color:#fff;background-color:#0063dc;border-color:#0063dc}.btn-flickr:not(:disabled):not(.disabled).active,.btn-flickr:not(:disabled):not(.disabled):active,.show>.btn-flickr.dropdown-toggle{color:#fff;background-color:#004ca9;border-color:#00469c}.btn-flickr:not(:disabled):not(.disabled).active:focus,.btn-flickr:not(:disabled):not(.disabled):active:focus,.show>.btn-flickr.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(0,99,220,.5)}.btn-bitbucket{color:#fff;background-color:#0052cc;border-color:#0052cc}.btn-bitbucket:hover{color:#fff;background-color:#0043a6;border-color:#003e99}.btn-bitbucket.focus,.btn-bitbucket:focus{box-shadow:0 0 0 2px rgba(0,82,204,.5)}.btn-bitbucket.disabled,.btn-bitbucket:disabled{color:#fff;background-color:#0052cc;border-color:#0052cc}.btn-bitbucket:not(:disabled):not(.disabled).active,.btn-bitbucket:not(:disabled):not(.disabled):active,.show>.btn-bitbucket.dropdown-toggle{color:#fff;background-color:#003e99;border-color:#00388c}.btn-bitbucket:not(:disabled):not(.disabled).active:focus,.btn-bitbucket:not(:disabled):not(.disabled):active:focus,.show>.btn-bitbucket.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(0,82,204,.5)}.btn-blue{color:#fff;background-color:#467fcf;border-color:#467fcf}.btn-blue:hover{color:#fff;background-color:#316cbe;border-color:#2f66b3}.btn-blue.focus,.btn-blue:focus{box-shadow:0 0 0 2px rgba(70,127,207,.5)}.btn-blue.disabled,.btn-blue:disabled{color:#fff;background-color:#467fcf;border-color:#467fcf}.btn-blue:not(:disabled):not(.disabled).active,.btn-blue:not(:disabled):not(.disabled):active,.show>.btn-blue.dropdown-toggle{color:#fff;background-color:#2f66b3;border-color:#2c60a9}.btn-blue:not(:disabled):not(.disabled).active:focus,.btn-blue:not(:disabled):not(.disabled):active:focus,.show>.btn-blue.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(70,127,207,.5)}.btn-indigo{color:#fff;background-color:#6574cd;border-color:#6574cd}.btn-indigo:hover{color:#fff;background-color:#485ac4;border-color:#3f51c1}.btn-indigo.focus,.btn-indigo:focus{box-shadow:0 0 0 2px rgba(101,116,205,.5)}.btn-indigo.disabled,.btn-indigo:disabled{color:#fff;background-color:#6574cd;border-color:#6574cd}.btn-indigo:not(:disabled):not(.disabled).active,.btn-indigo:not(:disabled):not(.disabled):active,.show>.btn-indigo.dropdown-toggle{color:#fff;background-color:#3f51c1;border-color:#3b4db7}.btn-indigo:not(:disabled):not(.disabled).active:focus,.btn-indigo:not(:disabled):not(.disabled):active:focus,.show>.btn-indigo.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(101,116,205,.5)}.btn-purple{color:#fff;background-color:#a55eea;border-color:#a55eea}.btn-purple:hover{color:#fff;background-color:#923ce6;border-color:#8c31e4}.btn-purple.focus,.btn-purple:focus{box-shadow:0 0 0 2px rgba(165,94,234,.5)}.btn-purple.disabled,.btn-purple:disabled{color:#fff;background-color:#a55eea;border-color:#a55eea}.btn-purple:not(:disabled):not(.disabled).active,.btn-purple:not(:disabled):not(.disabled):active,.show>.btn-purple.dropdown-toggle{color:#fff;background-color:#8c31e4;border-color:#8526e3}.btn-purple:not(:disabled):not(.disabled).active:focus,.btn-purple:not(:disabled):not(.disabled):active:focus,.show>.btn-purple.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(165,94,234,.5)}.btn-pink{color:#fff;background-color:#f66d9b;border-color:#f66d9b}.btn-pink:hover{color:#fff;background-color:#f44982;border-color:#f33d7a}.btn-pink.focus,.btn-pink:focus{box-shadow:0 0 0 2px rgba(246,109,155,.5)}.btn-pink.disabled,.btn-pink:disabled{color:#fff;background-color:#f66d9b;border-color:#f66d9b}.btn-pink:not(:disabled):not(.disabled).active,.btn-pink:not(:disabled):not(.disabled):active,.show>.btn-pink.dropdown-toggle{color:#fff;background-color:#f33d7a;border-color:#f23172}.btn-pink:not(:disabled):not(.disabled).active:focus,.btn-pink:not(:disabled):not(.disabled):active:focus,.show>.btn-pink.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(246,109,155,.5)}.btn-red{color:#fff;background-color:#cd201f;border-color:#cd201f}.btn-red:hover{color:#fff;background-color:#ac1b1a;border-color:#a11918}.btn-red.focus,.btn-red:focus{box-shadow:0 0 0 2px rgba(205,32,31,.5)}.btn-red.disabled,.btn-red:disabled{color:#fff;background-color:#cd201f;border-color:#cd201f}.btn-red:not(:disabled):not(.disabled).active,.btn-red:not(:disabled):not(.disabled):active,.show>.btn-red.dropdown-toggle{color:#fff;background-color:#a11918;border-color:#961717}.btn-red:not(:disabled):not(.disabled).active:focus,.btn-red:not(:disabled):not(.disabled):active:focus,.show>.btn-red.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(205,32,31,.5)}.btn-orange{color:#fff;background-color:#fd9644;border-color:#fd9644}.btn-orange:hover{color:#fff;background-color:#fd811e;border-color:#fc7a12}.btn-orange.focus,.btn-orange:focus{box-shadow:0 0 0 2px rgba(253,150,68,.5)}.btn-orange.disabled,.btn-orange:disabled{color:#fff;background-color:#fd9644;border-color:#fd9644}.btn-orange:not(:disabled):not(.disabled).active,.btn-orange:not(:disabled):not(.disabled):active,.show>.btn-orange.dropdown-toggle{color:#fff;background-color:#fc7a12;border-color:#fc7305}.btn-orange:not(:disabled):not(.disabled).active:focus,.btn-orange:not(:disabled):not(.disabled):active:focus,.show>.btn-orange.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(253,150,68,.5)}.btn-yellow{color:#fff;background-color:#f1c40f;border-color:#f1c40f}.btn-yellow:hover{color:#fff;background-color:#cea70c;border-color:#c29d0b}.btn-yellow.focus,.btn-yellow:focus{box-shadow:0 0 0 2px rgba(241,196,15,.5)}.btn-yellow.disabled,.btn-yellow:disabled{color:#fff;background-color:#f1c40f;border-color:#f1c40f}.btn-yellow:not(:disabled):not(.disabled).active,.btn-yellow:not(:disabled):not(.disabled):active,.show>.btn-yellow.dropdown-toggle{color:#fff;background-color:#c29d0b;border-color:#b6940b}.btn-yellow:not(:disabled):not(.disabled).active:focus,.btn-yellow:not(:disabled):not(.disabled):active:focus,.show>.btn-yellow.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(241,196,15,.5)}.btn-green{color:#fff;background-color:#5eba00;border-color:#5eba00}.btn-green:hover{color:#fff;background-color:#4b9400;border-color:#448700}.btn-green.focus,.btn-green:focus{box-shadow:0 0 0 2px rgba(94,186,0,.5)}.btn-green.disabled,.btn-green:disabled{color:#fff;background-color:#5eba00;border-color:#5eba00}.btn-green:not(:disabled):not(.disabled).active,.btn-green:not(:disabled):not(.disabled):active,.show>.btn-green.dropdown-toggle{color:#fff;background-color:#448700;border-color:#3e7a00}.btn-green:not(:disabled):not(.disabled).active:focus,.btn-green:not(:disabled):not(.disabled):active:focus,.show>.btn-green.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(94,186,0,.5)}.btn-teal{color:#fff;background-color:#2bcbba;border-color:#2bcbba}.btn-teal:hover{color:#fff;background-color:#24ab9d;border-color:#22a193}.btn-teal.focus,.btn-teal:focus{box-shadow:0 0 0 2px rgba(43,203,186,.5)}.btn-teal.disabled,.btn-teal:disabled{color:#fff;background-color:#2bcbba;border-color:#2bcbba}.btn-teal:not(:disabled):not(.disabled).active,.btn-teal:not(:disabled):not(.disabled):active,.show>.btn-teal.dropdown-toggle{color:#fff;background-color:#22a193;border-color:#20968a}.btn-teal:not(:disabled):not(.disabled).active:focus,.btn-teal:not(:disabled):not(.disabled):active:focus,.show>.btn-teal.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(43,203,186,.5)}.btn-cyan{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-cyan:hover{color:#fff;background-color:#138496;border-color:#117a8b}.btn-cyan.focus,.btn-cyan:focus{box-shadow:0 0 0 2px rgba(23,162,184,.5)}.btn-cyan.disabled,.btn-cyan:disabled{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-cyan:not(:disabled):not(.disabled).active,.btn-cyan:not(:disabled):not(.disabled):active,.show>.btn-cyan.dropdown-toggle{color:#fff;background-color:#117a8b;border-color:#10707f}.btn-cyan:not(:disabled):not(.disabled).active:focus,.btn-cyan:not(:disabled):not(.disabled):active:focus,.show>.btn-cyan.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(23,162,184,.5)}.btn-white{color:#495057;background-color:#fff;border-color:#fff}.btn-white:hover{color:#495057;background-color:#ececec;border-color:#e6e5e5}.btn-white.focus,.btn-white:focus{box-shadow:0 0 0 2px hsla(0,0%,100%,.5)}.btn-white.disabled,.btn-white:disabled{color:#495057;background-color:#fff;border-color:#fff}.btn-white:not(:disabled):not(.disabled).active,.btn-white:not(:disabled):not(.disabled):active,.show>.btn-white.dropdown-toggle{color:#495057;background-color:#e6e5e5;border-color:#dfdfdf}.btn-white:not(:disabled):not(.disabled).active:focus,.btn-white:not(:disabled):not(.disabled):active:focus,.show>.btn-white.dropdown-toggle:focus{box-shadow:0 0 0 2px hsla(0,0%,100%,.5)}.btn-gray{color:#fff;background-color:#868e96;border-color:#868e96}.btn-gray:hover{color:#fff;background-color:#727b84;border-color:#6c757d}.btn-gray.focus,.btn-gray:focus{box-shadow:0 0 0 2px rgba(134,142,150,.5)}.btn-gray.disabled,.btn-gray:disabled{color:#fff;background-color:#868e96;border-color:#868e96}.btn-gray:not(:disabled):not(.disabled).active,.btn-gray:not(:disabled):not(.disabled):active,.show>.btn-gray.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#666e76}.btn-gray:not(:disabled):not(.disabled).active:focus,.btn-gray:not(:disabled):not(.disabled):active:focus,.show>.btn-gray.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(134,142,150,.5)}.btn-gray-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-gray-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-gray-dark.focus,.btn-gray-dark:focus{box-shadow:0 0 0 2px rgba(52,58,64,.5)}.btn-gray-dark.disabled,.btn-gray-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-gray-dark:not(:disabled):not(.disabled).active,.btn-gray-dark:not(:disabled):not(.disabled):active,.show>.btn-gray-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-gray-dark:not(:disabled):not(.disabled).active:focus,.btn-gray-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-gray-dark.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(52,58,64,.5)}.btn-azure{color:#fff;background-color:#45aaf2;border-color:#45aaf2}.btn-azure:hover{color:#fff;background-color:#219af0;border-color:#1594ef}.btn-azure.focus,.btn-azure:focus{box-shadow:0 0 0 2px rgba(69,170,242,.5)}.btn-azure.disabled,.btn-azure:disabled{color:#fff;background-color:#45aaf2;border-color:#45aaf2}.btn-azure:not(:disabled):not(.disabled).active,.btn-azure:not(:disabled):not(.disabled):active,.show>.btn-azure.dropdown-toggle{color:#fff;background-color:#1594ef;border-color:#108ee7}.btn-azure:not(:disabled):not(.disabled).active:focus,.btn-azure:not(:disabled):not(.disabled):active:focus,.show>.btn-azure.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(69,170,242,.5)}.btn-lime{color:#fff;background-color:#7bd235;border-color:#7bd235}.btn-lime:hover{color:#fff;background-color:#69b829;border-color:#63ad27}.btn-lime.focus,.btn-lime:focus{box-shadow:0 0 0 2px rgba(123,210,53,.5)}.btn-lime.disabled,.btn-lime:disabled{color:#fff;background-color:#7bd235;border-color:#7bd235}.btn-lime:not(:disabled):not(.disabled).active,.btn-lime:not(:disabled):not(.disabled):active,.show>.btn-lime.dropdown-toggle{color:#fff;background-color:#63ad27;border-color:#5da324}.btn-lime:not(:disabled):not(.disabled).active:focus,.btn-lime:not(:disabled):not(.disabled):active:focus,.show>.btn-lime.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(123,210,53,.5)}.btn-option{background:transparent;color:#9aa0ac}.btn-option:hover{color:#6e7687}.btn-option:focus{box-shadow:none;color:#6e7687}.btn-group-sm>.btn,.btn-sm{font-size:.75rem;min-width:1.625rem}.btn-group-lg>.btn,.btn-lg{font-size:1rem;min-width:2.75rem;font-weight:400}.btn-list{margin-bottom:-.5rem;font-size:0}.btn-list>.btn,.btn-list>.dropdown{margin-bottom:.5rem}.btn-list>.btn:not(:last-child),.btn-list>.dropdown:not(:last-child){margin-right:.5rem}.btn-loading{color:transparent!important;pointer-events:none;position:relative}.btn-loading:after{content:"";-webkit-animation:loader .5s linear infinite;animation:loader .5s linear infinite;border:2px solid #fff;border-radius:50%;border-right-color:transparent!important;border-top-color:transparent!important;display:block;height:1.4em;width:1.4em;position:absolute;left:calc(50% - .7em);top:calc(50% - .7em);-webkit-transform-origin:center;transform-origin:center;position:absolute!important}.btn-group-sm>.btn-loading.btn:after,.btn-loading.btn-sm:after{height:1em;width:1em;left:calc(50% - .5em);top:calc(50% - .5em)}.btn-loading.btn-secondary:after{border-color:#495057}.alert{font-size:.9375rem}.alert-icon{padding-left:3rem}.alert-icon>i{color:inherit!important;font-size:1rem;position:absolute;top:1rem;left:1rem}.alert-avatar{padding-left:3.75rem}.alert-avatar .avatar{position:absolute;top:.5rem;left:.75rem}.close{font-size:1rem;line-height:1.5;transition:color .3s}.close:before{content:"\EA00";font-family:feather}.badge{color:#fff}.badge-default{background:#e9ecef;color:#868e96}.table thead th,.text-wrap table thead th{border-top:0;border-bottom-width:1px;padding-top:.5rem;padding-bottom:.5rem}.table th,.text-wrap table th{color:#9aa0ac;text-transform:uppercase;font-size:.875rem;font-weight:400}.table-md td,.table-md th{padding:.5rem}.table-vcenter td,.table-vcenter th{vertical-align:middle}.table-center td,.table-center th{text-align:center}.table-striped tbody tr:nth-of-type(odd){background:transparent}.table-striped tbody tr:nth-of-type(2n){background-color:rgba(0,0,0,.02)}.table-calendar{margin:0 0 .75rem}.table-calendar td,.table-calendar th{border:0;text-align:center;padding:0!important;width:14.28571429%;line-height:2.5rem}.table-calendar td{border-top:0}.table-calendar-link{line-height:2rem;min-width:calc(2rem + 2px);display:inline-block;border-radius:3px;background:#f8f9fa;color:#495057;font-weight:600;transition:background .3s,color .3s;position:relative}.table-calendar-link:before{content:"";width:4px;height:4px;position:absolute;left:.25rem;top:.25rem;border-radius:50px;background:#467fcf}.table-calendar-link:hover{color:#fff;text-decoration:none;background:#467fcf;transition:background .3s}.table-calendar-link:hover:before{background:#fff}.table-header{cursor:pointer;transition:color .3s}.table-header:hover{color:#495057!important}.table-header:after{content:"\F0DC";font-family:FontAwesome;display:inline-block;margin-left:.5rem;font-size:.75rem}.table-header-asc{color:#495057!important}.table-header-asc:after{content:"\F0DE"}.table-header-desc{color:#495057!important}.table-header-desc:after{content:"\F0DD"}.page-breadcrumb{background:none;padding:0;margin:1rem 0 0;font-size:.875rem}@media (min-width:768px){.page-breadcrumb{margin:-.5rem 0 0}}.page-breadcrumb .breadcrumb-item{color:#9aa0ac}.page-breadcrumb .breadcrumb-item.active{color:#6e7687}.pagination-simple .page-item .page-link{background:none;border:none}.pagination-simple .page-item.active .page-link{color:#495057;font-weight:700}.pagination-pager .page-prev{margin-right:auto}.pagination-pager .page-next{margin-left:auto}.page-total-text{margin-right:1rem;align-self:center;color:#6e7687}.card{box-shadow:0 1px 2px 0 rgba(0,0,0,.05);position:relative;margin-bottom:1.5rem;width:100%}.card .card{box-shadow:none}@media print{.card{box-shadow:none;border:none}}.card-body{flex:1 1 auto;margin:0;padding:1.5rem;position:relative}.card-body+.card-body{border-top:1px solid rgba(0,40,100,.12)}.card-body>:last-child{margin-bottom:0}@media print{.card-body{padding:0}}.card-body-scrollable{overflow:auto}.card-bottom,.card-footer{padding:1rem 1.5rem;background:none}.card-footer{border-top:1px solid rgba(0,40,100,.12);color:#6e7687}.card-header{background:none;padding:.5rem 1.5rem;display:flex;min-height:3.5rem;align-items:center}.card-header .card-title{margin-bottom:0}.card-header.border-0+.card-body{padding-top:0}@media print{.card-header{display:none}}.card-img-top{border-top-left-radius:3px;border-top-right-radius:3px}.card-img-overlay{background-color:rgba(0,0,0,.4);display:flex;flex-direction:column}.card-title{font-size:1.125rem;line-height:1.2;font-weight:400;margin-bottom:1.5rem}.card-title a{color:inherit}.card-title:only-child{margin-bottom:0}.card-subtitle,.card-title small{color:#9aa0ac;font-size:.875rem;display:block;margin:-.75rem 0 1rem;line-height:1.1;font-weight:400}.card-table{margin-bottom:0}.card-table tr:first-child td,.card-table tr:first-child th{border-top:0}.card-table tr td:first-child,.card-table tr th:first-child{padding-left:1.5rem}.card-table tr td:last-child,.card-table tr th:last-child{padding-right:1.5rem}.card-body+.card-table{border-top:1px solid rgba(0,40,100,.12)}.card-profile .card-header{height:9rem;background-size:cover}.card-profile-img{max-width:6rem;margin-top:-5rem;margin-bottom:1rem;border:3px solid #fff;border-radius:100%;box-shadow:0 1px 1px rgba(0,0,0,.1)}.card-link+.card-link{margin-left:1rem}.card-body+.card-list-group{border-top:1px solid rgba(0,40,100,.12)}.card-list-group .list-group-item{border-right:0;border-left:0;border-radius:0;padding-left:1.5rem;padding-right:1.5rem}.card-list-group .list-group-item:last-child{border-bottom:0}.card-list-group .list-group-item:first-child{border-top:0}.card-header-tabs{margin:-1.25rem 0;border-bottom:0;line-height:2rem}.card-header-tabs .nav-item{margin-bottom:1px}.card-header-pills{margin:-.75rem 0}.card-aside{flex-direction:row}.card-aside-column{min-width:5rem;width:30%;flex:0 0 30%;border-top-left-radius:3px;border-bottom-left-radius:3px;background:no-repeat 50%/cover}.card-value{font-size:2.5rem;line-height:3.4rem;height:3.4rem;display:flex;align-items:center;font-weight:400}.card-value i{vertical-align:middle}.card-chart-bg{height:4rem;margin-top:-1rem;position:relative;z-index:1;overflow:hidden}.card-options{margin-left:auto;display:flex;order:100;margin-right:-.5rem;color:#9aa0ac;align-self:center}.card-options a{margin-left:.5rem;color:#9aa0ac;display:inline-block;min-width:1rem}.card-options a:hover{text-decoration:none;color:#6e7687}.card-options a i{font-size:1rem;vertical-align:middle}.card-collapsed>:not(.card-header):not(.card-status),.card-options .dropdown-toggle:after{display:none}.card-collapsed .card-options-collapse i:before{content:"\E92D"}.card-fullscreen .card-options-fullscreen i:before{content:"\E992"}.card-fullscreen .card-options-remove{display:none}.card-map{height:15rem;background:#e9ecef}.card-map-placeholder{background:no-repeat 50%}.card-tabs{display:flex}.card-tabs-bottom .card-tabs-item{border:0;border-top:1px solid rgba(0,40,100,.12)}.card-tabs-bottom .card-tabs-item.active{border-top-color:#fff}.card-tabs-item{flex:1 1 auto;display:block;padding:1rem 1.5rem;border-bottom:1px solid rgba(0,40,100,.12);color:inherit;overflow:hidden}a.card-tabs-item{background:#fafbfc}a.card-tabs-item:hover{text-decoration:none;color:inherit}a.card-tabs-item:focus{z-index:1}a.card-tabs-item.active{background:#fff;border-bottom-color:#fff}.card-tabs-item+.card-tabs-item{border-left:1px solid rgba(0,40,100,.12)}.card-status{position:absolute;top:-1px;left:-1px;right:-1px;height:3px;border-radius:3px 3px 0 0;background:rgba(0,40,100,.12)}.card-status-left{right:auto;bottom:0;height:auto;width:3px;border-radius:3px 0 0 3px}.card-icon{width:3rem;font-size:2.5rem;line-height:3rem;text-align:center}.card-fullscreen{position:fixed;top:0;left:0;right:0;bottom:0;z-index:1;margin:0}.card-alert{border-radius:0;margin:-1px -1px 0}.card-category{font-size:.875rem;text-transform:uppercase;text-align:center;font-weight:600;letter-spacing:.05em;margin:0 0 .5rem}.popover{-webkit-filter:drop-shadow(0 1px 3px rgba(0,0,0,.1));filter:drop-shadow(0 1px 3px rgba(0,0,0,.1))}.popover.bs-popover-auto[x-placement^=top],.popover.bs-popover-top{margin-bottom:.625rem}.popover .arrow{margin-left:calc(.25rem + 2px)}.dropdown{display:inline-block}.dropdown-menu{box-shadow:0 1px 2px 0 rgba(0,0,0,.05);min-width:12rem}.dropdown-item{color:#6e7687}.dropdown-menu-arrow:before{top:-6px;border-bottom:5px solid rgba(0,0,0,.2)}.dropdown-menu-arrow:after,.dropdown-menu-arrow:before{position:absolute;left:12px;display:inline-block;border-right:5px solid transparent;border-left:5px solid transparent;content:""}.dropdown-menu-arrow:after{top:-5px;border-bottom:5px solid #fff}.dropdown-menu-arrow.dropdown-menu-right:after,.dropdown-menu-arrow.dropdown-menu-right:before{left:auto;right:12px}.dropdown-toggle{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:pointer}.dropdown-toggle:after{vertical-align:.155em}.dropdown-toggle:empty:after{margin-left:0}.dropdown-icon{color:#9aa0ac;margin-right:.5rem;margin-left:-.5rem;width:1em;display:inline-block;text-align:center;vertical-align:-1px}.list-inline-dots .list-inline-item+.list-inline-item:before{content:"\B7 ";margin-left:-2px;margin-right:3px}.list-separated-item{padding:1rem 0}.list-separated-item:first-child{padding-top:0}.list-separated-item:last-child{padding-bottom:0}.list-separated-item+.list-separated-item{border-top:1px solid rgba(0,40,100,.12)}.list-group-item.active .icon{color:inherit!important}.list-group-transparent .list-group-item{background:none;border:0;padding:.5rem 1rem;border-radius:3px}.list-group-transparent .list-group-item.active{background:rgba(70,127,207,.06);font-weight:600}.avatar{width:2rem;height:2rem;line-height:2rem;border-radius:50%;display:inline-block;background:#ced4da no-repeat 50%/cover;position:relative;text-align:center;color:#868e96;font-weight:600;vertical-align:bottom;font-size:.875rem;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.avatar i{font-size:125%;vertical-align:sub}.avatar-status{position:absolute;right:-2px;bottom:-2px;width:.75rem;height:.75rem;border:2px solid #fff;background:#868e96;border-radius:50%}.avatar-sm{width:1.5rem;height:1.5rem;line-height:1.5rem;font-size:.75rem}.avatar-md{width:2.5rem;height:2.5rem;line-height:2.5rem;font-size:1rem}.avatar-lg{width:3rem;height:3rem;line-height:3rem;font-size:1.25rem}.avatar-xl{width:4rem;height:4rem;line-height:4rem;font-size:1.75rem}.avatar-xxl{width:5rem;height:5rem;line-height:5rem;font-size:2rem}.avatar-placeholder{background:#ced4da url('data:image/svg+xml;charset=utf8,') no-repeat 50%/80%}.avatar-list{margin:0 0 -.5rem;padding:0;font-size:0}.avatar-list .avatar{margin-bottom:.5rem}.avatar-list .avatar:not(:last-child){margin-right:.5rem}.avatar-list-stacked .avatar{margin-right:-.8em!important;box-shadow:0 0 0 2px #fff}.avatar-blue{background-color:#c8d9f1;color:#467fcf}.avatar-indigo{background-color:#d1d5f0;color:#6574cd}.avatar-purple{background-color:#e4cff9;color:#a55eea}.avatar-pink{background-color:#fcd3e1;color:#f66d9b}.avatar-red{background-color:#f0bcbc;color:#cd201f}.avatar-orange{background-color:#fee0c7;color:#fd9644}.avatar-yellow{background-color:#fbedb7;color:#f1c40f}.avatar-green{background-color:#cfeab3;color:#5eba00}.avatar-teal{background-color:#bfefea;color:#2bcbba}.avatar-cyan{background-color:#b9e3ea;color:#17a2b8}.avatar-white{background-color:#fff;color:#fff}.avatar-gray{background-color:#dbdde0;color:#868e96}.avatar-gray-dark{background-color:#c2c4c6;color:#343a40}.avatar-azure{background-color:#c7e6fb;color:#45aaf2}.avatar-lime{background-color:#d7f2c2;color:#7bd235}.product-price{font-size:1rem}.product-price strong{font-size:1.5rem}@-webkit-keyframes indeterminate{0%{left:-35%;right:100%}60%,to{left:100%;right:-90%}}@keyframes indeterminate{0%{left:-35%;right:100%}60%,to{left:100%;right:-90%}}@-webkit-keyframes indeterminate-short{0%{left:-200%;right:100%}60%,to{left:107%;right:-8%}}@keyframes indeterminate-short{0%{left:-200%;right:100%}60%,to{left:107%;right:-8%}}.progress{position:relative}.progress-xs,.progress-xs .progress-bar{height:.25rem}.progress-sm,.progress-sm .progress-bar{height:.5rem}.progress-bar-indeterminate:after,.progress-bar-indeterminate:before{content:"";position:absolute;background-color:inherit;left:0;will-change:left,right;top:0;bottom:0}.progress-bar-indeterminate:before{-webkit-animation:indeterminate 2.1s cubic-bezier(.65,.815,.735,.395) infinite;animation:indeterminate 2.1s cubic-bezier(.65,.815,.735,.395) infinite}.progress-bar-indeterminate:after{-webkit-animation:indeterminate-short 2.1s cubic-bezier(.165,.84,.44,1) infinite;animation:indeterminate-short 2.1s cubic-bezier(.165,.84,.44,1) infinite;-webkit-animation-delay:1.15s;animation-delay:1.15s}@-webkit-keyframes loader{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes loader{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.dimmer{position:relative}.dimmer .loader{display:none;margin:0 auto;position:absolute;top:50%;left:0;right:0;-webkit-transform:translateY(-50%);transform:translateY(-50%)}.dimmer.active .loader{display:block}.dimmer.active .dimmer-content{opacity:.04;pointer-events:none}.loader{display:block;position:relative;height:2.5rem;width:2.5rem;color:#467fcf}.loader:after,.loader:before{width:2.5rem;height:2.5rem;margin:-1.25rem 0 0 -1.25rem;position:absolute;content:"";top:50%;left:50%}.loader:before{border-radius:50%;border:3px solid;opacity:.15}.loader:after{-webkit-animation:loader .6s linear;animation:loader .6s linear;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite;border-radius:50%;border:3px solid transparent;border-top-color:currentcolor;box-shadow:0 0 0 1px transparent}.icons-list{list-style:none;margin:0 -1px -1px 0;padding:0;display:flex;flex-wrap:wrap}.icons-list>li{flex:1 0 4rem}.icons-list-wrap{overflow:hidden}.icons-list-item{text-align:center;height:4rem;display:flex;align-items:center;justify-content:center;border-right:1px solid rgba(0,40,100,.12);border-bottom:1px solid rgba(0,40,100,.12)}.icons-list-item i{font-size:1.25rem}.img-gallery{margin-right:-.25rem;margin-left:-.25rem;margin-bottom:-.5rem}.img-gallery>.col,.img-gallery>[class*=col-]{padding-left:.25rem;padding-right:.25rem;padding-bottom:.5rem}.link-overlay{position:relative}.link-overlay:hover .link-overlay-bg{opacity:1}.link-overlay-bg{position:absolute;top:0;left:0;right:0;bottom:0;background:rgba(70,127,207,.8);display:flex;color:#fff;align-items:center;justify-content:center;font-size:1.25rem;opacity:0;transition:opacity .3s}.media-icon{width:2rem;height:2rem;line-height:2rem;text-align:center;border-radius:100%}.media-list{margin:0;padding:0;list-style:none}textarea[cols]{height:auto}.form-group,.form-label{display:block}.form-label{margin-bottom:.375rem;font-weight:600;font-size:.875rem}.form-label-small{float:right;font-weight:400;font-size:87.5%}.form-footer{margin-top:2rem}.custom-control{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.custom-controls-stacked .custom-control{margin-bottom:.25rem}.custom-control-label{vertical-align:middle}.custom-control-label:before{border:1px solid rgba(0,40,100,.12);background-color:#fff;background-size:.5rem}.custom-control-description{line-height:1.5rem}.input-group-append,.input-group-btn,.input-group-prepend{font-size:.9375rem}.input-group-append>.btn,.input-group-btn>.btn,.input-group-prepend>.btn{height:100%;border-color:rgba(0,40,100,.12)}.input-group-prepend>.input-group-text{border-right:0}.input-group-append>.input-group-text{border-left:0}.input-icon{position:relative}.input-icon .form-control:not(:last-child){padding-right:2.5rem}.input-icon .form-control:not(:first-child){padding-left:2.5rem}.input-icon-addon{position:absolute;top:0;bottom:0;left:0;color:#9aa0ac;display:flex;align-items:center;justify-content:center;min-width:2.5rem;pointer-events:none}.input-icon-addon:last-child{left:auto;right:0}.form-fieldset{background:#f8f9fa;border:1px solid #e9ecef;padding:1rem;border-radius:3px;margin-bottom:1rem}.form-required{color:#cd201f}.form-required:before{content:" "}.state-valid{padding-right:2rem;background:url("data:image/svg+xml;charset=utf8,") no-repeat center right .5rem/1rem}.state-invalid{padding-right:2rem;background:url("data:image/svg+xml;charset=utf8,") no-repeat center right .5rem/1rem}.form-help{display:inline-block;width:1rem;height:1rem;text-align:center;line-height:1rem;color:#9aa0ac;background:#f8f9fa;border-radius:50%;font-size:.75rem;transition:background-color .3s,color .3s;text-decoration:none;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.form-help:hover,.form-help[aria-describedby]{background:#467fcf;color:#fff}.sparkline{display:inline-block;height:2rem}.jqstooltip{box-sizing:initial;font-family:inherit!important;background:#333!important;border:none!important;border-radius:3px;font-size:11px!important;font-weight:700!important;line-height:1!important;padding:6px!important}.jqstooltip .jqsfield{font:inherit!important}.social-links li a{background:#f8f8f8;border-radius:50%;color:#9aa0ac;display:inline-block;height:1.75rem;width:1.75rem;line-height:1.75rem;text-align:center}.chart,.map{position:relative;padding-top:56.25%}.chart-square,.map-square{padding-top:100%}.chart-content,.map-content{position:absolute;top:0;left:0;right:0;bottom:0}.map-header{margin-top:-1.5rem;height:15rem;position:relative;margin-bottom:-1.5rem}.map-header:before{content:"";position:absolute;bottom:0;left:0;right:0;height:10rem;background:linear-gradient(180deg,rgba(245,247,251,0) 5%,#f5f7fb 95%);pointer-events:none}.map-header-layer{height:100%}.map-static{height:120px;width:100%;max-width:640px;background-position:50%;background-size:640px 120px}@-webkit-keyframes status-pulse{0%,to{opacity:1}50%{opacity:.32}}@keyframes status-pulse{0%,to{opacity:1}50%{opacity:.32}}.status-icon{content:"";width:.5rem;height:.5rem;display:inline-block;background:currentColor;border-radius:50%;-webkit-transform:translateY(-1px);transform:translateY(-1px);margin-right:.375rem;vertical-align:middle}.status-animated{-webkit-animation:status-pulse 1s ease infinite;animation:status-pulse 1s ease infinite}.chart-circle{display:block;height:8rem;width:8rem;position:relative}.chart-circle canvas{margin:0 auto;display:block;max-width:100%;max-height:100%}.chart-circle-xs{height:2.5rem;width:2.5rem;font-size:.8rem}.chart-circle-sm{height:4rem;width:4rem;font-size:.8rem}.chart-circle-lg{height:10rem;width:10rem;font-size:.8rem}.chart-circle-value{position:absolute;top:0;left:0;right:0;margin-left:auto;margin-right:auto;bottom:0;display:flex;justify-content:center;align-items:center;flex-direction:column;line-height:1}.chart-circle-value small{display:block;color:#9aa0ac;font-size:.9375rem}.chips{margin:0 0 -.5rem}.chips .chip{margin:0 .5rem .5rem 0}.chip{display:inline-block;height:2rem;line-height:2rem;font-size:.875rem;font-weight:500;color:#6e7687;padding:0 .75rem;border-radius:1rem;background-color:#f8f9fa;transition:background .3s}.chip .avatar{float:left;margin:0 .5rem 0 -.75rem;height:2rem;width:2rem;border-radius:50%}a.chip:hover{color:inherit;text-decoration:none;background-color:#e9ecef}.stamp{color:#fff;background:#868e96;display:inline-block;min-width:2rem;height:2rem;padding:0 .25rem;line-height:2rem;text-align:center;border-radius:3px;font-weight:600}.stamp-md{min-width:2.5rem;height:2.5rem;line-height:2.5rem}.chat{outline:0;margin:0;list-style-type:none;flex-direction:column;justify-content:flex-end;min-height:100%}.chat,.chat-line{padding:0;display:flex}.chat-line{text-align:right;position:relative;flex-direction:row-reverse}.chat-line+.chat-line{padding-top:1rem}.chat-message{position:relative;display:inline-block;background-color:#467fcf;color:#fff;font-size:.875rem;padding:.375rem .5rem;border-radius:3px;white-space:normal;text-align:left;margin:0 .5rem 0 2.5rem;line-height:1.4}.chat-message>:last-child{margin-bottom:0!important}.chat-message:after{content:"";position:absolute;right:-5px;top:7px;border-bottom:6px solid transparent;border-left:6px solid #467fcf;border-top:6px solid transparent}.chat-message img{max-width:100%}.chat-message p{margin-bottom:1em}.chat-line-friend{flex-direction:row}.chat-line-friend+.chat-line-friend{margin-top:-.5rem}.chat-line-friend+.chat-line-friend .chat-author{visibility:hidden}.chat-line-friend+.chat-line-friend .chat-message:after{display:none}.chat-line-friend .chat-message{background-color:#f3f3f3;color:#495057;margin-left:.5rem;margin-right:2.5rem}.chat-line-friend .chat-message:after{right:auto;left:-5px;border-left-width:0;border-right:5px solid #f3f3f3}.example{padding:1.5rem;border:1px solid rgba(0,40,100,.12);border-radius:3px 3px 0 0;font-size:.9375rem}.example-bg{background:#f5f7fb}.example+.highlight{border-top:none;margin-top:0;border-radius:0 0 3px 3px}.highlight{margin:1rem 0 2rem;border:1px solid rgba(0,40,100,.12);border-radius:3px;font-size:.9375rem;max-height:40rem;overflow:auto;background:#fcfcfc}.highlight pre{margin-bottom:0;background-color:initial}.example-column{margin:0 auto}.example-column>.card:last-of-type{margin-bottom:0}.example-column-1{max-width:20rem}.example-column-2{max-width:40rem}.tag{font-size:.75rem;color:#6e7687;background-color:#e9ecef;border-radius:3px;padding:0 .5rem;line-height:2em;display:inline-flex;cursor:default;font-weight:400;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}a.tag{text-decoration:none;cursor:pointer;transition:color .3s,background .3s}a.tag:hover{background-color:rgba(110,118,135,.2);color:inherit}.tag-addon{display:inline-block;padding:0 .5rem;color:inherit;text-decoration:none;background:rgba(0,0,0,.06);margin:0 -.5rem 0 .5rem;text-align:center;min-width:1.5rem}.tag-addon:last-child{border-top-right-radius:3px;border-bottom-right-radius:3px}.tag-addon i{vertical-align:middle;margin:0 -.25rem}a.tag-addon{text-decoration:none;cursor:pointer;transition:color .3s,background .3s}a.tag-addon:hover{background:rgba(0,0,0,.16);color:inherit}.tag-avatar{width:1.5rem;height:1.5rem;border-radius:3px 0 0 3px;margin:0 .5rem 0 -.5rem}.tag-blue{background-color:#467fcf;color:#fff}.tag-indigo{background-color:#6574cd;color:#fff}.tag-purple{background-color:#a55eea;color:#fff}.tag-pink{background-color:#f66d9b;color:#fff}.tag-red{background-color:#cd201f;color:#fff}.tag-orange{background-color:#fd9644;color:#fff}.tag-yellow{background-color:#f1c40f;color:#fff}.tag-green{background-color:#5eba00;color:#fff}.tag-teal{background-color:#2bcbba;color:#fff}.tag-cyan{background-color:#17a2b8;color:#fff}.tag-white{background-color:#fff;color:#fff}.tag-gray{background-color:#868e96;color:#fff}.tag-gray-dark{background-color:#343a40;color:#fff}.tag-azure{background-color:#45aaf2;color:#fff}.tag-lime{background-color:#7bd235;color:#fff}.tag-primary{background-color:#467fcf;color:#fff}.tag-secondary{background-color:#868e96;color:#fff}.tag-success{background-color:#5eba00;color:#fff}.tag-info{background-color:#45aaf2;color:#fff}.tag-warning{background-color:#f1c40f;color:#fff}.tag-danger{background-color:#cd201f;color:#fff}.tag-light{background-color:#f8f9fa;color:#fff}.tag-dark{background-color:#343a40;color:#fff}.tag-rounded,.tag-rounded .tag-avatar{border-radius:50px}.tags{margin-bottom:-.5rem;font-size:0}.tags>.tag{margin-bottom:.5rem}.tags>.tag:not(:last-child){margin-right:.5rem}.highlight .hll{background-color:#ffc}.highlight .c{color:#999}.highlight .k{color:#069}.highlight .o{color:#555}.highlight .cm{color:#999}.highlight .cp{color:#099}.highlight .c1,.highlight .cs{color:#999}.highlight .gd{background-color:#fcc;border:1px solid #c00}.highlight .ge{font-style:italic}.highlight .gr{color:red}.highlight .gh{color:#030}.highlight .gi{background-color:#cfc;border:1px solid #0c0}.highlight .go{color:#aaa}.highlight .gp{color:#009}.highlight .gu{color:#030}.highlight .gt{color:#9c6}.highlight .kc,.highlight .kd,.highlight .kn,.highlight .kp,.highlight .kr{color:#069}.highlight .kt{color:#078}.highlight .m{color:#f60}.highlight .s{color:#d44950}.highlight .na{color:#4f9fcf}.highlight .nb{color:#366}.highlight .nc{color:#0a8}.highlight .no{color:#360}.highlight .nd{color:#99f}.highlight .ni{color:#999}.highlight .ne{color:#c00}.highlight .nf{color:#c0f}.highlight .nl{color:#99f}.highlight .nn{color:#0cf}.highlight .nt{color:#2f6f9f}.highlight .nv{color:#033}.highlight .ow{color:#000}.highlight .w{color:#bbb}.highlight .mf,.highlight .mh,.highlight .mi,.highlight .mo{color:#f60}.highlight .sb,.highlight .sc{color:#c30}.highlight .sd{font-style:italic;color:#c30}.highlight .s2,.highlight .se,.highlight .sh{color:#c30}.highlight .si{color:#a00}.highlight .sx{color:#c30}.highlight .sr{color:#3aa}.highlight .s1{color:#c30}.highlight .ss{color:#fc3}.highlight .bp{color:#366}.highlight .vc,.highlight .vg,.highlight .vi{color:#033}.highlight .il{color:#f60}.highlight .css .nt+.nt,.highlight .css .o,.highlight .css .o+.nt{color:#999}.highlight .language-bash:before,.highlight .language-sh:before{color:#009;content:"$ ";-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.highlight .language-powershell:before{color:#009;content:"PM> ";-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.custom-range{align-items:center;-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;cursor:pointer;display:flex;height:100%;min-height:2.375rem;overflow:hidden;padding:0;border:0}.custom-range:focus{box-shadow:none;outline:none}.custom-range:focus::-webkit-slider-thumb{border-color:#467fcf;background-color:#467fcf}.custom-range:focus::-moz-range-thumb{border-color:#467fcf;background-color:#467fcf}.custom-range:focus::-ms-thumb{border-color:#467fcf;background-color:#467fcf}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-runnable-track{background:#467fcf;content:"";height:2px;pointer-events:none}.custom-range::-webkit-slider-thumb{width:14px;height:14px;-webkit-appearance:none;appearance:none;background:#fff;border-radius:50px;box-shadow:1px 0 0 -6px rgba(0,50,126,.12),6px 0 0 -6px rgba(0,50,126,.12),7px 0 0 -6px rgba(0,50,126,.12),8px 0 0 -6px rgba(0,50,126,.12),9px 0 0 -6px rgba(0,50,126,.12),10px 0 0 -6px rgba(0,50,126,.12),11px 0 0 -6px rgba(0,50,126,.12),12px 0 0 -6px rgba(0,50,126,.12),13px 0 0 -6px rgba(0,50,126,.12),14px 0 0 -6px rgba(0,50,126,.12),15px 0 0 -6px rgba(0,50,126,.12),16px 0 0 -6px rgba(0,50,126,.12),17px 0 0 -6px rgba(0,50,126,.12),18px 0 0 -6px rgba(0,50,126,.12),19px 0 0 -6px rgba(0,50,126,.12),20px 0 0 -6px rgba(0,50,126,.12),21px 0 0 -6px rgba(0,50,126,.12),22px 0 0 -6px rgba(0,50,126,.12),23px 0 0 -6px rgba(0,50,126,.12),24px 0 0 -6px rgba(0,50,126,.12),25px 0 0 -6px rgba(0,50,126,.12),26px 0 0 -6px rgba(0,50,126,.12),27px 0 0 -6px rgba(0,50,126,.12),28px 0 0 -6px rgba(0,50,126,.12),29px 0 0 -6px rgba(0,50,126,.12),30px 0 0 -6px rgba(0,50,126,.12),31px 0 0 -6px rgba(0,50,126,.12),32px 0 0 -6px rgba(0,50,126,.12),33px 0 0 -6px rgba(0,50,126,.12),34px 0 0 -6px rgba(0,50,126,.12),35px 0 0 -6px rgba(0,50,126,.12),36px 0 0 -6px rgba(0,50,126,.12),37px 0 0 -6px rgba(0,50,126,.12),38px 0 0 -6px rgba(0,50,126,.12),39px 0 0 -6px rgba(0,50,126,.12),40px 0 0 -6px rgba(0,50,126,.12),41px 0 0 -6px rgba(0,50,126,.12),42px 0 0 -6px rgba(0,50,126,.12),43px 0 0 -6px rgba(0,50,126,.12),44px 0 0 -6px rgba(0,50,126,.12),45px 0 0 -6px rgba(0,50,126,.12),46px 0 0 -6px rgba(0,50,126,.12),47px 0 0 -6px rgba(0,50,126,.12),48px 0 0 -6px rgba(0,50,126,.12),49px 0 0 -6px rgba(0,50,126,.12),50px 0 0 -6px rgba(0,50,126,.12),51px 0 0 -6px rgba(0,50,126,.12),52px 0 0 -6px rgba(0,50,126,.12),53px 0 0 -6px rgba(0,50,126,.12),54px 0 0 -6px rgba(0,50,126,.12),55px 0 0 -6px rgba(0,50,126,.12),56px 0 0 -6px rgba(0,50,126,.12),57px 0 0 -6px rgba(0,50,126,.12),58px 0 0 -6px rgba(0,50,126,.12),59px 0 0 -6px rgba(0,50,126,.12),60px 0 0 -6px rgba(0,50,126,.12),61px 0 0 -6px rgba(0,50,126,.12),62px 0 0 -6px rgba(0,50,126,.12),63px 0 0 -6px rgba(0,50,126,.12),64px 0 0 -6px rgba(0,50,126,.12),65px 0 0 -6px rgba(0,50,126,.12),66px 0 0 -6px rgba(0,50,126,.12),67px 0 0 -6px rgba(0,50,126,.12),68px 0 0 -6px rgba(0,50,126,.12),69px 0 0 -6px rgba(0,50,126,.12),70px 0 0 -6px rgba(0,50,126,.12),71px 0 0 -6px rgba(0,50,126,.12),72px 0 0 -6px rgba(0,50,126,.12),73px 0 0 -6px rgba(0,50,126,.12),74px 0 0 -6px rgba(0,50,126,.12),75px 0 0 -6px rgba(0,50,126,.12),76px 0 0 -6px rgba(0,50,126,.12),77px 0 0 -6px rgba(0,50,126,.12),78px 0 0 -6px rgba(0,50,126,.12),79px 0 0 -6px rgba(0,50,126,.12),80px 0 0 -6px rgba(0,50,126,.12),81px 0 0 -6px rgba(0,50,126,.12),82px 0 0 -6px rgba(0,50,126,.12),83px 0 0 -6px rgba(0,50,126,.12),84px 0 0 -6px rgba(0,50,126,.12),85px 0 0 -6px rgba(0,50,126,.12),86px 0 0 -6px rgba(0,50,126,.12),87px 0 0 -6px rgba(0,50,126,.12),88px 0 0 -6px rgba(0,50,126,.12),89px 0 0 -6px rgba(0,50,126,.12),90px 0 0 -6px rgba(0,50,126,.12),91px 0 0 -6px rgba(0,50,126,.12),92px 0 0 -6px rgba(0,50,126,.12),93px 0 0 -6px rgba(0,50,126,.12),94px 0 0 -6px rgba(0,50,126,.12),95px 0 0 -6px rgba(0,50,126,.12),96px 0 0 -6px rgba(0,50,126,.12),97px 0 0 -6px rgba(0,50,126,.12),98px 0 0 -6px rgba(0,50,126,.12),99px 0 0 -6px rgba(0,50,126,.12),100px 0 0 -6px rgba(0,50,126,.12),101px 0 0 -6px rgba(0,50,126,.12),102px 0 0 -6px rgba(0,50,126,.12),103px 0 0 -6px rgba(0,50,126,.12),104px 0 0 -6px rgba(0,50,126,.12),105px 0 0 -6px rgba(0,50,126,.12),106px 0 0 -6px rgba(0,50,126,.12),107px 0 0 -6px rgba(0,50,126,.12),108px 0 0 -6px rgba(0,50,126,.12),109px 0 0 -6px rgba(0,50,126,.12),110px 0 0 -6px rgba(0,50,126,.12),111px 0 0 -6px rgba(0,50,126,.12),112px 0 0 -6px rgba(0,50,126,.12),113px 0 0 -6px rgba(0,50,126,.12),114px 0 0 -6px rgba(0,50,126,.12),115px 0 0 -6px rgba(0,50,126,.12),116px 0 0 -6px rgba(0,50,126,.12),117px 0 0 -6px rgba(0,50,126,.12),118px 0 0 -6px rgba(0,50,126,.12),119px 0 0 -6px rgba(0,50,126,.12),120px 0 0 -6px rgba(0,50,126,.12),121px 0 0 -6px rgba(0,50,126,.12),122px 0 0 -6px rgba(0,50,126,.12),123px 0 0 -6px rgba(0,50,126,.12),124px 0 0 -6px rgba(0,50,126,.12),125px 0 0 -6px rgba(0,50,126,.12),126px 0 0 -6px rgba(0,50,126,.12),127px 0 0 -6px rgba(0,50,126,.12),128px 0 0 -6px rgba(0,50,126,.12),129px 0 0 -6px rgba(0,50,126,.12),130px 0 0 -6px rgba(0,50,126,.12),131px 0 0 -6px rgba(0,50,126,.12),132px 0 0 -6px rgba(0,50,126,.12),133px 0 0 -6px rgba(0,50,126,.12),134px 0 0 -6px rgba(0,50,126,.12),135px 0 0 -6px rgba(0,50,126,.12),136px 0 0 -6px rgba(0,50,126,.12),137px 0 0 -6px rgba(0,50,126,.12),138px 0 0 -6px rgba(0,50,126,.12),139px 0 0 -6px rgba(0,50,126,.12),140px 0 0 -6px rgba(0,50,126,.12),141px 0 0 -6px rgba(0,50,126,.12),142px 0 0 -6px rgba(0,50,126,.12),143px 0 0 -6px rgba(0,50,126,.12),144px 0 0 -6px rgba(0,50,126,.12),145px 0 0 -6px rgba(0,50,126,.12),146px 0 0 -6px rgba(0,50,126,.12),147px 0 0 -6px rgba(0,50,126,.12),148px 0 0 -6px rgba(0,50,126,.12),149px 0 0 -6px rgba(0,50,126,.12),150px 0 0 -6px rgba(0,50,126,.12),151px 0 0 -6px rgba(0,50,126,.12),152px 0 0 -6px rgba(0,50,126,.12),153px 0 0 -6px rgba(0,50,126,.12),154px 0 0 -6px rgba(0,50,126,.12),155px 0 0 -6px rgba(0,50,126,.12),156px 0 0 -6px rgba(0,50,126,.12),157px 0 0 -6px rgba(0,50,126,.12),158px 0 0 -6px rgba(0,50,126,.12),159px 0 0 -6px rgba(0,50,126,.12),160px 0 0 -6px rgba(0,50,126,.12),161px 0 0 -6px rgba(0,50,126,.12),162px 0 0 -6px rgba(0,50,126,.12),163px 0 0 -6px rgba(0,50,126,.12),164px 0 0 -6px rgba(0,50,126,.12),165px 0 0 -6px rgba(0,50,126,.12),166px 0 0 -6px rgba(0,50,126,.12),167px 0 0 -6px rgba(0,50,126,.12),168px 0 0 -6px rgba(0,50,126,.12),169px 0 0 -6px rgba(0,50,126,.12),170px 0 0 -6px rgba(0,50,126,.12),171px 0 0 -6px rgba(0,50,126,.12),172px 0 0 -6px rgba(0,50,126,.12),173px 0 0 -6px rgba(0,50,126,.12),174px 0 0 -6px rgba(0,50,126,.12),175px 0 0 -6px rgba(0,50,126,.12),176px 0 0 -6px rgba(0,50,126,.12),177px 0 0 -6px rgba(0,50,126,.12),178px 0 0 -6px rgba(0,50,126,.12),179px 0 0 -6px rgba(0,50,126,.12),180px 0 0 -6px rgba(0,50,126,.12),181px 0 0 -6px rgba(0,50,126,.12),182px 0 0 -6px rgba(0,50,126,.12),183px 0 0 -6px rgba(0,50,126,.12),184px 0 0 -6px rgba(0,50,126,.12),185px 0 0 -6px rgba(0,50,126,.12),186px 0 0 -6px rgba(0,50,126,.12),187px 0 0 -6px rgba(0,50,126,.12),188px 0 0 -6px rgba(0,50,126,.12),189px 0 0 -6px rgba(0,50,126,.12),190px 0 0 -6px rgba(0,50,126,.12),191px 0 0 -6px rgba(0,50,126,.12),192px 0 0 -6px rgba(0,50,126,.12),193px 0 0 -6px rgba(0,50,126,.12),194px 0 0 -6px rgba(0,50,126,.12),195px 0 0 -6px rgba(0,50,126,.12),196px 0 0 -6px rgba(0,50,126,.12),197px 0 0 -6px rgba(0,50,126,.12),198px 0 0 -6px rgba(0,50,126,.12),199px 0 0 -6px rgba(0,50,126,.12),200px 0 0 -6px rgba(0,50,126,.12),201px 0 0 -6px rgba(0,50,126,.12),202px 0 0 -6px rgba(0,50,126,.12),203px 0 0 -6px rgba(0,50,126,.12),204px 0 0 -6px rgba(0,50,126,.12),205px 0 0 -6px rgba(0,50,126,.12),206px 0 0 -6px rgba(0,50,126,.12),207px 0 0 -6px rgba(0,50,126,.12),208px 0 0 -6px rgba(0,50,126,.12),209px 0 0 -6px rgba(0,50,126,.12),210px 0 0 -6px rgba(0,50,126,.12),211px 0 0 -6px rgba(0,50,126,.12),212px 0 0 -6px rgba(0,50,126,.12),213px 0 0 -6px rgba(0,50,126,.12),214px 0 0 -6px rgba(0,50,126,.12),215px 0 0 -6px rgba(0,50,126,.12),216px 0 0 -6px rgba(0,50,126,.12),217px 0 0 -6px rgba(0,50,126,.12),218px 0 0 -6px rgba(0,50,126,.12),219px 0 0 -6px rgba(0,50,126,.12),220px 0 0 -6px rgba(0,50,126,.12),221px 0 0 -6px rgba(0,50,126,.12),222px 0 0 -6px rgba(0,50,126,.12),223px 0 0 -6px rgba(0,50,126,.12),224px 0 0 -6px rgba(0,50,126,.12),225px 0 0 -6px rgba(0,50,126,.12),226px 0 0 -6px rgba(0,50,126,.12),227px 0 0 -6px rgba(0,50,126,.12),228px 0 0 -6px rgba(0,50,126,.12),229px 0 0 -6px rgba(0,50,126,.12),230px 0 0 -6px rgba(0,50,126,.12),231px 0 0 -6px rgba(0,50,126,.12),232px 0 0 -6px rgba(0,50,126,.12),233px 0 0 -6px rgba(0,50,126,.12),234px 0 0 -6px rgba(0,50,126,.12),235px 0 0 -6px rgba(0,50,126,.12),236px 0 0 -6px rgba(0,50,126,.12),237px 0 0 -6px rgba(0,50,126,.12),238px 0 0 -6px rgba(0,50,126,.12),239px 0 0 -6px rgba(0,50,126,.12),240px 0 0 -6px rgba(0,50,126,.12);margin-top:-6px;border:1px solid rgba(0,30,75,.12);transition:border-color .3s,background-color .3s}.custom-range::-moz-range-track{width:240px;height:2px;background:rgba(0,50,126,.12)}.custom-range::-moz-range-thumb{width:14px;height:14px;background:#fff;border-radius:50px;border:1px solid rgba(0,30,75,.12);position:relative;transition:border-color .3s,background-color .3s}.custom-range::-moz-range-progress{height:2px;background:#467fcf;border:0;margin-top:0}.custom-range::-ms-track{background:transparent;border:0;border-color:transparent;border-radius:0;border-width:0;color:transparent;height:2px;margin-top:10px;width:240px}.custom-range::-ms-thumb{width:240px;height:2px;background:#fff;border-radius:50px;border:1px solid rgba(0,30,75,.12);transition:border-color .3s,background-color .3s}.custom-range::-ms-fill-lower{background:#467fcf;border-radius:0}.custom-range::-ms-fill-upper{background:rgba(0,50,126,.12);border-radius:0}.custom-range::-ms-tooltip{display:none}.selectgroup{display:inline-flex}.selectgroup-item{flex-grow:1;position:relative}.selectgroup-item+.selectgroup-item{margin-left:-1px}.selectgroup-item:not(:first-child) .selectgroup-button{border-top-left-radius:0;border-bottom-left-radius:0}.selectgroup-item:not(:last-child) .selectgroup-button{border-top-right-radius:0;border-bottom-right-radius:0}.selectgroup-input{opacity:0;position:absolute;z-index:-1;top:0;left:0}.selectgroup-button{display:block;border:1px solid rgba(0,40,100,.12);text-align:center;padding:.375rem 1rem;position:relative;cursor:pointer;border-radius:3px;color:#9aa0ac;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;font-size:.9375rem;line-height:1.5;min-width:2.375rem}.selectgroup-button-icon{padding-left:.5rem;padding-right:.5rem;font-size:1.125rem;line-height:1.125rem}.selectgroup-input:checked+.selectgroup-button{border-color:#467fcf;z-index:1;color:#467fcf;background:#edf2fa}.selectgroup-input:focus+.selectgroup-button{border-color:#467fcf;z-index:2;color:#467fcf;box-shadow:0 0 0 2px rgba(70,127,207,.25)}.selectgroup-pills{flex-wrap:wrap;align-items:flex-start}.selectgroup-pills .selectgroup-item{margin-right:.5rem;flex-grow:0}.selectgroup-pills .selectgroup-button{border-radius:50px!important}.custom-switch{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:default;display:inline-flex;align-items:center;margin:0}.custom-switch-input{position:absolute;z-index:-1;opacity:0}.custom-switches-stacked{display:flex;flex-direction:column}.custom-switches-stacked .custom-switch{margin-bottom:.5rem}.custom-switch-indicator{display:inline-block;height:1.25rem;width:2.25rem;background:#e9ecef;border-radius:50px;position:relative;vertical-align:bottom;border:1px solid rgba(0,40,100,.12);transition:border-color .3s,background-color .3s}.custom-switch-indicator:before{content:"";position:absolute;height:calc(1.25rem - 4px);width:calc(1.25rem - 4px);top:1px;left:1px;background:#fff;border-radius:50%;transition:left .3s;box-shadow:0 1px 2px 0 rgba(0,0,0,.4)}.custom-switch-input:checked~.custom-switch-indicator{background:#467fcf}.custom-switch-input:checked~.custom-switch-indicator:before{left:calc(1rem + 1px)}.custom-switch-input:focus~.custom-switch-indicator{box-shadow:0 0 0 2px rgba(70,127,207,.25);border-color:#467fcf}.custom-switch-description{margin-left:.5rem;color:#6e7687;transition:color .3s}.custom-switch-input:checked~.custom-switch-description{color:#495057}.imagecheck{margin:0;position:relative;cursor:pointer}.imagecheck-input{position:absolute;z-index:-1;opacity:0}.imagecheck-figure{border:1px solid rgba(0,40,100,.12);border-radius:3px;margin:0;position:relative}.imagecheck-input:focus~.imagecheck-figure{border-color:#467fcf;box-shadow:0 0 0 2px rgba(70,127,207,.25)}.imagecheck-input:checked~.imagecheck-figure{border-color:rgba(0,40,100,.24)}.imagecheck-figure:before{content:"";position:absolute;top:.25rem;left:.25rem;display:block;width:1rem;height:1rem;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background:#467fcf url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E") no-repeat 50%/50% 50%;color:#fff;z-index:1;border-radius:3px;opacity:0;transition:opacity .3s}.imagecheck-input:checked~.imagecheck-figure:before{opacity:1}.imagecheck-image{max-width:100%;opacity:.64;transition:opacity .3s}.imagecheck-image:first-child{border-top-left-radius:2px;border-top-right-radius:2px}.imagecheck-image:last-child{border-bottom-left-radius:2px;border-bottom-right-radius:2px}.imagecheck-input:checked~.imagecheck-figure .imagecheck-image,.imagecheck-input:focus~.imagecheck-figure .imagecheck-image,.imagecheck:hover .imagecheck-image{opacity:1}.imagecheck-caption{text-align:center;padding:.25rem;color:#9aa0ac;font-size:.875rem;transition:color .3s}.imagecheck-input:checked~.imagecheck-figure .imagecheck-caption,.imagecheck-input:focus~.imagecheck-figure .imagecheck-caption,.imagecheck:hover .imagecheck-caption{color:#495057}.colorinput{margin:0;position:relative;cursor:pointer}.colorinput-input{position:absolute;z-index:-1;opacity:0}.colorinput-color{display:inline-block;width:1.75rem;height:1.75rem;border-radius:3px;border:1px solid rgba(0,40,100,.12);color:#fff;box-shadow:0 1px 2px 0 rgba(0,0,0,.05)}.colorinput-color:before{content:"";opacity:0;position:absolute;top:.25rem;left:.25rem;height:1.25rem;width:1.25rem;transition:opacity .3s;background:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E") no-repeat 50%/50% 50%}.colorinput-input:checked~.colorinput-color:before{opacity:1}.colorinput-input:focus~.colorinput-color{border-color:#467fcf;box-shadow:0 0 0 2px rgba(70,127,207,.25)}.timeline{position:relative;margin:0 0 2rem;padding:0;list-style:none}.timeline:before{background-color:#e9ecef;position:absolute;display:block;content:"";width:1px;height:100%;top:0;bottom:0;left:4px}.timeline-item{position:relative;display:flex;padding-left:2rem;margin:.5rem 0}.timeline-item:first-child:before,.timeline-item:last-child:before{content:"";position:absolute;background:#fff;width:1px;left:.25rem}.timeline-item:first-child{margin-top:0}.timeline-item:first-child:before{top:0;height:.5rem}.timeline-item:last-child{margin-bottom:0}.timeline-item:last-child:before{top:.5rem;bottom:0}.timeline-badge{position:absolute;display:block;width:.4375rem;height:.4375rem;left:1px;top:.5rem;border-radius:100%;border:1px solid #fff;background:#adb5bd}.timeline-time{white-space:nowrap;margin-left:auto;color:#9aa0ac;font-size:87.5%}.browser{width:1.25rem;height:1.25rem;display:inline-block;background:no-repeat 50%/100% 100%;vertical-align:bottom;font-style:normal}.browser-android-browser{background-image:url(/static/media/android-browser.e1d3686c.svg)}.browser-aol-explorer{background-image:url(/static/media/aol-explorer.f2a4363b.svg)}.browser-blackberry{background-image:url(/static/media/blackberry.ead509ae.svg)}.browser-camino{background-image:url(/static/media/camino.23c5c7fa.svg)}.browser-chrome{background-image:url(/static/media/chrome.2bbe801c.svg)}.browser-chromium{background-image:url(/static/media/chromium.870087fd.svg)}.browser-dolphin{background-image:url(/static/media/dolphin.f66d5a06.svg)}.browser-edge{background-image:url(/static/media/edge.abda4ac1.svg)}.browser-firefox{background-image:url(/static/media/firefox.e037fac5.svg)}.browser-ie{background-image:url(/static/media/ie.57c3e539.svg)}.browser-maxthon{background-image:url(/static/media/maxthon.df51f6f4.svg)}.browser-mozilla{background-image:url(/static/media/mozilla.91974b40.svg)}.browser-netscape{background-image:url(/static/media/netscape.f64e6793.svg)}.browser-opera{background-image:url(/static/media/opera.438992de.svg)}.browser-safari{background-image:url(/static/media/safari.ee79ab6a.svg)}.browser-sleipnir{background-image:url(/static/media/sleipnir.1751c6d6.svg)}.browser-uc-browser{background-image:url(/static/media/uc-browser.f600350d.svg)}.browser-vivaldi{background-image:url(/static/media/vivaldi.6b04dfda.svg)}.flag{width:1.6rem;height:1.2rem;display:inline-block;background:no-repeat 50%/100% 100%;vertical-align:bottom;font-style:normal;box-shadow:0 0 1px 1px rgba(0,0,0,.1);border-radius:2px}.flag-ad{background-image:url(/static/media/ad.30f99f82.svg)}.flag-ae{background-image:url(/static/media/ae.1f331bd9.svg)}.flag-af{background-image:url(/static/media/af.a8df755f.svg)}.flag-ag{background-image:url(/static/media/ag.7cb635f0.svg)}.flag-ai{background-image:url(/static/media/ai.928b5a4f.svg)}.flag-al{background-image:url(/static/media/al.1c4942df.svg)}.flag-am{background-image:url(/static/media/am.af917f4b.svg)}.flag-ao{background-image:url(/static/media/ao.fd948d03.svg)}.flag-aq{background-image:url(/static/media/aq.fb98f0e6.svg)}.flag-ar{background-image:url(/static/media/ar.2ed2ee2a.svg)}.flag-as{background-image:url(/static/media/as.e18a5953.svg)}.flag-at{background-image:url(/static/media/at.511e196f.svg)}.flag-au{background-image:url(/static/media/au.b853c2eb.svg)}.flag-aw{background-image:url(/static/media/aw.dc91764d.svg)}.flag-ax{background-image:url(/static/media/ax.3301f616.svg)}.flag-az{background-image:url(/static/media/az.ba2d1e5e.svg)}.flag-ba{background-image:url(/static/media/ba.a441d8da.svg)}.flag-bb{background-image:url(/static/media/bb.c568edd5.svg)}.flag-bd{background-image:url(/static/media/bd.b12e3060.svg)}.flag-be{background-image:url(/static/media/be.fb18617c.svg)}.flag-bf{background-image:url(/static/media/bf.f88288fa.svg)}.flag-bg{background-image:url(/static/media/bg.bc04745d.svg)}.flag-bh{background-image:url(/static/media/bh.805f2682.svg)}.flag-bi{background-image:url(/static/media/bi.bc8085f9.svg)}.flag-bj{background-image:url(/static/media/bj.ea52986c.svg)}.flag-bl{background-image:url(/static/media/bl.a5c508b2.svg)}.flag-bm{background-image:url(/static/media/bm.6339387e.svg)}.flag-bn{background-image:url(/static/media/bn.51104069.svg)}.flag-bo{background-image:url(/static/media/bo.e02afe04.svg)}.flag-bq{background-image:url(/static/media/bq.4cac15ed.svg)}.flag-br{background-image:url(/static/media/br.fc7b8290.svg)}.flag-bs{background-image:url(/static/media/bs.421969c2.svg)}.flag-bt{background-image:url(/static/media/bt.39149c62.svg)}.flag-bv{background-image:url(/static/media/bv.58761e89.svg)}.flag-bw{background-image:url(/static/media/bw.8ecb0b8e.svg)}.flag-by{background-image:url(/static/media/by.6fd2caab.svg)}.flag-bz{background-image:url(/static/media/bz.e86e9bd2.svg)}.flag-ca{background-image:url(/static/media/ca.af259017.svg)}.flag-cc{background-image:url(/static/media/cc.ec7f3820.svg)}.flag-cd{background-image:url(/static/media/cd.020e3d1e.svg)}.flag-cf{background-image:url(/static/media/cf.f75250a7.svg)}.flag-cg{background-image:url(/static/media/cg.497d91d1.svg)}.flag-ch{background-image:url(/static/media/ch.d5161894.svg)}.flag-ci{background-image:url(/static/media/ci.1334b221.svg)}.flag-ck{background-image:url(/static/media/ck.869edc71.svg)}.flag-cl{background-image:url(/static/media/cl.9d5227cb.svg)}.flag-cm{background-image:url(/static/media/cm.17f2e2c9.svg)}.flag-cn{background-image:url(/static/media/cn.c2814ac0.svg)}.flag-co{background-image:url(/static/media/co.433d22ad.svg)}.flag-cr{background-image:url(/static/media/cr.20a9e6bf.svg)}.flag-cu{background-image:url(/static/media/cu.050058cb.svg)}.flag-cv{background-image:url(/static/media/cv.6b699492.svg)}.flag-cw{background-image:url(/static/media/cw.07a0d3f9.svg)}.flag-cx{background-image:url(/static/media/cx.5180dbe5.svg)}.flag-cy{background-image:url(/static/media/cy.657ef7aa.svg)}.flag-cz{background-image:url(/static/media/cz.6731f872.svg)}.flag-de{background-image:url(/static/media/de.01e89f77.svg)}.flag-dj{background-image:url(/static/media/dj.f4c086cc.svg)}.flag-dk{background-image:url(/static/media/dk.44761537.svg)}.flag-dm{background-image:url(/static/media/dm.89c91dc6.svg)}.flag-do{background-image:url(/static/media/do.d8ab6db9.svg)}.flag-dz{background-image:url(/static/media/dz.333db1ef.svg)}.flag-ec{background-image:url(/static/media/ec.918e57b8.svg)}.flag-ee{background-image:url(/static/media/ee.57f366b0.svg)}.flag-eg{background-image:url(/static/media/eg.07f2e96d.svg)}.flag-eh{background-image:url(/static/media/eh.e4f13505.svg)}.flag-er{background-image:url(/static/media/er.70738db6.svg)}.flag-es{background-image:url(/static/media/es.c6ca5440.svg)}.flag-et{background-image:url(/static/media/et.31aa0fc0.svg)}.flag-eu{background-image:url(/static/media/eu.17beaf81.svg)}.flag-fi{background-image:url(/static/media/fi.58bcc4af.svg)}.flag-fj{background-image:url(/static/media/fj.b1ddba60.svg)}.flag-fk{background-image:url(/static/media/fk.5c64395d.svg)}.flag-fm{background-image:url(/static/media/fm.2bd7d4df.svg)}.flag-fo{background-image:url(/static/media/fo.dc9ed815.svg)}.flag-fr{background-image:url(/static/media/fr.a178bcfb.svg)}.flag-ga{background-image:url(/static/media/ga.33442fb9.svg)}.flag-gb-eng{background-image:url(/static/media/gb-eng.a933214c.svg)}.flag-gb-nir{background-image:url(/static/media/gb-nir.943d406a.svg)}.flag-gb-sct{background-image:url(/static/media/gb-sct.772350bf.svg)}.flag-gb-wls{background-image:url(/static/media/gb-wls.2831a6dd.svg)}.flag-gb{background-image:url(/static/media/gb.5638bbd9.svg)}.flag-gd{background-image:url(/static/media/gd.c17d779e.svg)}.flag-ge{background-image:url(/static/media/ge.334a8275.svg)}.flag-gf{background-image:url(/static/media/gf.4ea8e159.svg)}.flag-gg{background-image:url(/static/media/gg.d339aeb2.svg)}.flag-gh{background-image:url(/static/media/gh.d4b35e14.svg)}.flag-gi{background-image:url(/static/media/gi.c9543d40.svg)}.flag-gl{background-image:url(/static/media/gl.d02c42ea.svg)}.flag-gm{background-image:url(/static/media/gm.9423800e.svg)}.flag-gn{background-image:url(/static/media/gn.e472dff7.svg)}.flag-gp{background-image:url(/static/media/gp.a178bcfb.svg)}.flag-gq{background-image:url(/static/media/gq.6bbb0e76.svg)}.flag-gr{background-image:url(/static/media/gr.9a9a62a1.svg)}.flag-gs{background-image:url(/static/media/gs.37216917.svg)}.flag-gt{background-image:url(/static/media/gt.0b689ffe.svg)}.flag-gu{background-image:url(/static/media/gu.ad34e604.svg)}.flag-gw{background-image:url(/static/media/gw.e1d47aa4.svg)}.flag-gy{background-image:url(/static/media/gy.19bcfc34.svg)}.flag-hk{background-image:url(/static/media/hk.fb606eb1.svg)}.flag-hm{background-image:url(/static/media/hm.b43f3857.svg)}.flag-hn{background-image:url(/static/media/hn.3d726baa.svg)}.flag-hr{background-image:url(/static/media/hr.79e564a4.svg)}.flag-ht{background-image:url(/static/media/ht.d0404e4a.svg)}.flag-hu{background-image:url(/static/media/hu.a8abaf37.svg)}.flag-id{background-image:url(/static/media/id.ee020a0f.svg)}.flag-ie{background-image:url(/static/media/ie.d609c4e7.svg)}.flag-il{background-image:url(/static/media/il.0ea7e9da.svg)}.flag-im{background-image:url(/static/media/im.19884f0c.svg)}.flag-in{background-image:url(/static/media/in.2d667fbb.svg)}.flag-io{background-image:url(/static/media/io.2e0c61df.svg)}.flag-iq{background-image:url(/static/media/iq.61fca184.svg)}.flag-ir{background-image:url(/static/media/ir.3cb275a7.svg)}.flag-is{background-image:url(/static/media/is.ec1fb876.svg)}.flag-it{background-image:url(/static/media/it.bd6b5ff3.svg)}.flag-je{background-image:url(/static/media/je.6a9e1b93.svg)}.flag-jm{background-image:url(/static/media/jm.7db0ffd8.svg)}.flag-jo{background-image:url(/static/media/jo.d1405940.svg)}.flag-jp{background-image:url(/static/media/jp.fd264681.svg)}.flag-ke{background-image:url(/static/media/ke.15b698f3.svg)}.flag-kg{background-image:url(/static/media/kg.de33c048.svg)}.flag-kh{background-image:url(/static/media/kh.bfffb443.svg)}.flag-ki{background-image:url(/static/media/ki.fbe824dc.svg)}.flag-km{background-image:url(/static/media/km.cd351374.svg)}.flag-kn{background-image:url(/static/media/kn.7ab9462c.svg)}.flag-kp{background-image:url(/static/media/kp.b2729dfa.svg)}.flag-kr{background-image:url(/static/media/kr.32f23faf.svg)}.flag-kw{background-image:url(/static/media/kw.3e24a94a.svg)}.flag-ky{background-image:url(/static/media/ky.f7c3a515.svg)}.flag-kz{background-image:url(/static/media/kz.529db212.svg)}.flag-la{background-image:url(/static/media/la.bdfc4ab5.svg)}.flag-lb{background-image:url(/static/media/lb.49819740.svg)}.flag-lc{background-image:url(/static/media/lc.6c2940da.svg)}.flag-li{background-image:url(/static/media/li.10e0d5b2.svg)}.flag-lk{background-image:url(/static/media/lk.f0a4f4f6.svg)}.flag-lr{background-image:url(/static/media/lr.5485e606.svg)}.flag-ls{background-image:url(/static/media/ls.700ddad0.svg)}.flag-lt{background-image:url(/static/media/lt.14b63eab.svg)}.flag-lu{background-image:url(/static/media/lu.06956a13.svg)}.flag-lv{background-image:url(/static/media/lv.83353fa9.svg)}.flag-ly{background-image:url(/static/media/ly.ededce32.svg)}.flag-ma{background-image:url(/static/media/ma.8c27c493.svg)}.flag-mc{background-image:url(/static/media/mc.4241d3ff.svg)}.flag-md{background-image:url(/static/media/md.f9aceffb.svg)}.flag-me{background-image:url(/static/media/me.399015d8.svg)}.flag-mf{background-image:url(/static/media/mf.a178bcfb.svg)}.flag-mg{background-image:url(/static/media/mg.0c0da5f0.svg)}.flag-mh{background-image:url(/static/media/mh.a3bb001b.svg)}.flag-mk{background-image:url(/static/media/mk.29cb0cb2.svg)}.flag-ml{background-image:url(/static/media/ml.be076fd9.svg)}.flag-mm{background-image:url(/static/media/mm.e6d7c5a4.svg)}.flag-mn{background-image:url(/static/media/mn.cfd48e45.svg)}.flag-mo{background-image:url(/static/media/mo.36f1d6f2.svg)}.flag-mp{background-image:url(/static/media/mp.fcdc8e39.svg)}.flag-mq{background-image:url(/static/media/mq.4c4286cd.svg)}.flag-mr{background-image:url(/static/media/mr.6b3d082d.svg)}.flag-ms{background-image:url(/static/media/ms.8b73c710.svg)}.flag-mt{background-image:url(/static/media/mt.cffcad79.svg)}.flag-mu{background-image:url(/static/media/mu.974b9e6c.svg)}.flag-mv{background-image:url(/static/media/mv.e343afe8.svg)}.flag-mw{background-image:url(/static/media/mw.5b33db84.svg)}.flag-mx{background-image:url(/static/media/mx.184d53d1.svg)}.flag-my{background-image:url(/static/media/my.aae5bd9c.svg)}.flag-mz{background-image:url(/static/media/mz.cd1e97af.svg)}.flag-na{background-image:url(/static/media/na.f38aead1.svg)}.flag-nc{background-image:url(/static/media/nc.a2dc6650.svg)}.flag-ne{background-image:url(/static/media/ne.bad21adc.svg)}.flag-nf{background-image:url(/static/media/nf.fc2d0f07.svg)}.flag-ng{background-image:url(/static/media/ng.2ddc320b.svg)}.flag-ni{background-image:url(/static/media/ni.2b983496.svg)}.flag-nl{background-image:url(/static/media/nl.de2a39a2.svg)}.flag-no{background-image:url(/static/media/no.8331157c.svg)}.flag-np{background-image:url(/static/media/np.e6de6946.svg)}.flag-nr{background-image:url(/static/media/nr.f2afa5b9.svg)}.flag-nu{background-image:url(/static/media/nu.e6bfaa15.svg)}.flag-nz{background-image:url(/static/media/nz.03d7410a.svg)}.flag-om{background-image:url(/static/media/om.9b7a06b9.svg)}.flag-pa{background-image:url(/static/media/pa.91076135.svg)}.flag-pe{background-image:url(/static/media/pe.4cabbfc6.svg)}.flag-pf{background-image:url(/static/media/pf.28a15c37.svg)}.flag-pg{background-image:url(/static/media/pg.e444f903.svg)}.flag-ph{background-image:url(/static/media/ph.8b5fbe69.svg)}.flag-pk{background-image:url(/static/media/pk.db891066.svg)}.flag-pl{background-image:url(/static/media/pl.2257cff6.svg)}.flag-pm{background-image:url(/static/media/pm.a2dc6650.svg)}.flag-pn{background-image:url(/static/media/pn.bf813bfe.svg)}.flag-pr{background-image:url(/static/media/pr.e489537c.svg)}.flag-ps{background-image:url(/static/media/ps.225ede35.svg)}.flag-pt{background-image:url(/static/media/pt.e129260b.svg)}.flag-pw{background-image:url(/static/media/pw.0557592e.svg)}.flag-py{background-image:url(/static/media/py.abc5b396.svg)}.flag-qa{background-image:url(/static/media/qa.20a4d741.svg)}.flag-re{background-image:url(/static/media/re.a2dc6650.svg)}.flag-ro{background-image:url(/static/media/ro.552b5d97.svg)}.flag-rs{background-image:url(/static/media/rs.426b1d47.svg)}.flag-ru{background-image:url(/static/media/ru.517e32a1.svg)}.flag-rw{background-image:url(/static/media/rw.46fb809f.svg)}.flag-sa{background-image:url(/static/media/sa.67b058ae.svg)}.flag-sb{background-image:url(/static/media/sb.115ce3e5.svg)}.flag-sc{background-image:url(/static/media/sc.fdc11a48.svg)}.flag-sd{background-image:url(/static/media/sd.a14badd5.svg)}.flag-se{background-image:url(/static/media/se.22475f52.svg)}.flag-sg{background-image:url(/static/media/sg.22b0739e.svg)}.flag-sh{background-image:url(/static/media/sh.0726abdb.svg)}.flag-si{background-image:url(/static/media/si.72f83c29.svg)}.flag-sj{background-image:url(/static/media/sj.8331157c.svg)}.flag-sk{background-image:url(/static/media/sk.f44daf85.svg)}.flag-sl{background-image:url(/static/media/sl.835d44f6.svg)}.flag-sm{background-image:url(/static/media/sm.f3eb4474.svg)}.flag-sn{background-image:url(/static/media/sn.4dc603d1.svg)}.flag-so{background-image:url(/static/media/so.3bdb1de2.svg)}.flag-sr{background-image:url(/static/media/sr.65cdb1de.svg)}.flag-ss{background-image:url(/static/media/ss.0c7c9ffc.svg)}.flag-st{background-image:url(/static/media/st.230410b5.svg)}.flag-sv{background-image:url(/static/media/sv.a21150d5.svg)}.flag-sx{background-image:url(/static/media/sx.d23d1807.svg)}.flag-sy{background-image:url(/static/media/sy.0fedea07.svg)}.flag-sz{background-image:url(/static/media/sz.1ae99e45.svg)}.flag-tc{background-image:url(/static/media/tc.2f7d308e.svg)}.flag-td{background-image:url(/static/media/td.079a2525.svg)}.flag-tf{background-image:url(/static/media/tf.adc24fb2.svg)}.flag-tg{background-image:url(/static/media/tg.b96ee542.svg)}.flag-th{background-image:url(/static/media/th.50269587.svg)}.flag-tj{background-image:url(/static/media/tj.b6533ad3.svg)}.flag-tk{background-image:url(/static/media/tk.22d4831b.svg)}.flag-tl{background-image:url(/static/media/tl.f563fdae.svg)}.flag-tm{background-image:url(/static/media/tm.d2132088.svg)}.flag-tn{background-image:url(/static/media/tn.ef273685.svg)}.flag-to{background-image:url(/static/media/to.fa884203.svg)}.flag-tr{background-image:url(/static/media/tr.aabe02c2.svg)}.flag-tt{background-image:url(/static/media/tt.f09daa6d.svg)}.flag-tv{background-image:url(/static/media/tv.1a077ad0.svg)}.flag-tw{background-image:url(/static/media/tw.7baefd1c.svg)}.flag-tz{background-image:url(/static/media/tz.d5c9c20a.svg)}.flag-ua{background-image:url(/static/media/ua.acc88be0.svg)}.flag-ug{background-image:url(/static/media/ug.1e070275.svg)}.flag-um{background-image:url(/static/media/um.a1fa2de3.svg)}.flag-un{background-image:url(/static/media/un.1519b6c6.svg)}.flag-us{background-image:url(/static/media/us.2382ea7e.svg)}.flag-uy{background-image:url(/static/media/uy.a7e91b40.svg)}.flag-uz{background-image:url(/static/media/uz.791dfbda.svg)}.flag-va{background-image:url(/static/media/va.6b139c75.svg)}.flag-vc{background-image:url(/static/media/vc.f3912357.svg)}.flag-ve{background-image:url(/static/media/ve.6f48a1b9.svg)}.flag-vg{background-image:url(/static/media/vg.3b3121b2.svg)}.flag-vi{background-image:url(/static/media/vi.b3c0a20f.svg)}.flag-vn{background-image:url(/static/media/vn.0b7571b8.svg)}.flag-vu{background-image:url(/static/media/vu.9a6c3abc.svg)}.flag-wf{background-image:url(/static/media/wf.4b4f5462.svg)}.flag-ws{background-image:url(/static/media/ws.23b64335.svg)}.flag-ye{background-image:url(/static/media/ye.55897575.svg)}.flag-yt{background-image:url(/static/media/yt.a2dc6650.svg)}.flag-za{background-image:url(/static/media/za.d8ffed67.svg)}.flag-zm{background-image:url(/static/media/zm.62586634.svg)}.flag-zw{background-image:url(/static/media/zw.e223cee5.svg)}.payment{width:2.5rem;height:1.5rem;display:inline-block;background:no-repeat 50%/100% 100%;vertical-align:bottom;font-style:normal;box-shadow:0 0 1px 1px rgba(0,0,0,.1);border-radius:2px}.payment-2checkout-dark{background-image:url(/static/media/2checkout-dark.65d58d80.svg)}.payment-2checkout{background-image:url(/static/media/2checkout.e14c0f5e.svg)}.payment-alipay-dark{background-image:url(/static/media/alipay-dark.b6a651d2.svg)}.payment-alipay{background-image:url(/static/media/alipay.31580e28.svg)}.payment-amazon-dark{background-image:url(/static/media/amazon-dark.b178a57f.svg)}.payment-amazon{background-image:url(/static/media/amazon.5c500045.svg)}.payment-americanexpress-dark{background-image:url(/static/media/americanexpress-dark.c2ea2d77.svg)}.payment-americanexpress{background-image:url(/static/media/americanexpress.b89abdaf.svg)}.payment-applepay-dark{background-image:url(/static/media/applepay-dark.e044dbdb.svg)}.payment-applepay{background-image:url(/static/media/applepay.1ff3d3f0.svg)}.payment-bancontact-dark{background-image:url(/static/media/bancontact-dark.6e786090.svg)}.payment-bancontact{background-image:url(/static/media/bancontact.8c0a0fa2.svg)}.payment-bitcoin-dark{background-image:url(/static/media/bitcoin-dark.edaf60e1.svg)}.payment-bitcoin{background-image:url(/static/media/bitcoin.d9ac7b61.svg)}.payment-bitpay-dark{background-image:url(/static/media/bitpay-dark.f86a15da.svg)}.payment-bitpay{background-image:url(/static/media/bitpay.ffb94e65.svg)}.payment-cirrus-dark{background-image:url(/static/media/cirrus-dark.243a362e.svg)}.payment-cirrus{background-image:url(/static/media/cirrus.983db5f2.svg)}.payment-clickandbuy-dark{background-image:url(/static/media/clickandbuy-dark.f7d38984.svg)}.payment-clickandbuy{background-image:url(/static/media/clickandbuy.eb61d075.svg)}.payment-coinkite-dark{background-image:url(/static/media/coinkite-dark.f50deb17.svg)}.payment-coinkite{background-image:url(/static/media/coinkite.b6098277.svg)}.payment-dinersclub-dark{background-image:url(/static/media/dinersclub-dark.baff56e3.svg)}.payment-dinersclub{background-image:url(/static/media/dinersclub.45249b1d.svg)}.payment-directdebit-dark{background-image:url(/static/media/directdebit-dark.bf510996.svg)}.payment-directdebit{background-image:url(/static/media/directdebit.37695b62.svg)}.payment-discover-dark{background-image:url(/static/media/discover-dark.00f5c21f.svg)}.payment-discover{background-image:url(/static/media/discover.2f4fe159.svg)}.payment-dwolla-dark{background-image:url(/static/media/dwolla-dark.ccae2767.svg)}.payment-dwolla{background-image:url(/static/media/dwolla.36f57770.svg)}.payment-ebay-dark{background-image:url(/static/media/ebay-dark.bd7ccde1.svg)}.payment-ebay{background-image:url(/static/media/ebay.862b611a.svg)}.payment-eway-dark{background-image:url(/static/media/eway-dark.bbf15466.svg)}.payment-eway{background-image:url(/static/media/eway.54d6e672.svg)}.payment-giropay-dark{background-image:url(/static/media/giropay-dark.ff3c753a.svg)}.payment-giropay{background-image:url(/static/media/giropay.7337d9d0.svg)}.payment-googlewallet-dark{background-image:url(/static/media/googlewallet-dark.7cbe03be.svg)}.payment-googlewallet{background-image:url(/static/media/googlewallet.7f0e39ad.svg)}.payment-ingenico-dark{background-image:url(/static/media/ingenico-dark.5bef3895.svg)}.payment-ingenico{background-image:url(/static/media/ingenico.20a24d68.svg)}.payment-jcb-dark{background-image:url(/static/media/jcb-dark.f9bf701d.svg)}.payment-jcb{background-image:url(/static/media/jcb.2646bc51.svg)}.payment-klarna-dark{background-image:url(/static/media/klarna-dark.3a666a1e.svg)}.payment-klarna{background-image:url(/static/media/klarna.c05b3bba.svg)}.payment-laser-dark{background-image:url(/static/media/laser-dark.758bd7b6.svg)}.payment-laser{background-image:url(/static/media/laser.4642dfb3.svg)}.payment-maestro-dark{background-image:url(/static/media/maestro-dark.0d91ff8f.svg)}.payment-maestro{background-image:url(/static/media/maestro.31a202b4.svg)}.payment-mastercard-dark{background-image:url(/static/media/mastercard-dark.b1695f2b.svg)}.payment-mastercard{background-image:url(/static/media/mastercard.a6684d93.svg)}.payment-monero-dark{background-image:url(/static/media/monero-dark.29d40dee.svg)}.payment-monero{background-image:url(/static/media/monero.7df16d08.svg)}.payment-neteller-dark{background-image:url(/static/media/neteller-dark.63736cac.svg)}.payment-neteller{background-image:url(/static/media/neteller.798e0b4b.svg)}.payment-ogone-dark{background-image:url(/static/media/ogone-dark.5fa709fb.svg)}.payment-ogone{background-image:url(/static/media/ogone.8832c251.svg)}.payment-okpay-dark{background-image:url(/static/media/okpay-dark.26eabf7a.svg)}.payment-okpay{background-image:url(/static/media/okpay.72f763a2.svg)}.payment-paybox-dark{background-image:url(/static/media/paybox-dark.321bd555.svg)}.payment-paybox{background-image:url(/static/media/paybox.46f8af3b.svg)}.payment-paymill-dark{background-image:url(/static/media/paymill-dark.d8737b88.svg)}.payment-paymill{background-image:url(/static/media/paymill.6f906616.svg)}.payment-payone-dark{background-image:url(/static/media/payone-dark.992480f1.svg)}.payment-payone{background-image:url(/static/media/payone.2c68e11e.svg)}.payment-payoneer-dark{background-image:url(/static/media/payoneer-dark.8d95de50.svg)}.payment-payoneer{background-image:url(/static/media/payoneer.e460ab6b.svg)}.payment-paypal-dark{background-image:url(/static/media/paypal-dark.2abbaed4.svg)}.payment-paypal{background-image:url(/static/media/paypal.aa9749d2.svg)}.payment-paysafecard-dark{background-image:url(/static/media/paysafecard-dark.2a3832c3.svg)}.payment-paysafecard{background-image:url(/static/media/paysafecard.0db2bc55.svg)}.payment-payu-dark{background-image:url(/static/media/payu-dark.80265cc7.svg)}.payment-payu{background-image:url(/static/media/payu.ece9e639.svg)}.payment-payza-dark{background-image:url(/static/media/payza-dark.aaf8d63f.svg)}.payment-payza{background-image:url(/static/media/payza.05716451.svg)}.payment-ripple-dark{background-image:url(/static/media/ripple-dark.a741b2b1.svg)}.payment-ripple{background-image:url(/static/media/ripple.44f32f32.svg)}.payment-sage-dark{background-image:url(/static/media/sage-dark.1560c69d.svg)}.payment-sage{background-image:url(/static/media/sage.c962e60b.svg)}.payment-sepa-dark{background-image:url(/static/media/sepa-dark.3834e619.svg)}.payment-sepa{background-image:url(/static/media/sepa.45d27bde.svg)}.payment-shopify-dark{background-image:url(/static/media/shopify-dark.937412fd.svg)}.payment-shopify{background-image:url(/static/media/shopify.2a87d23f.svg)}.payment-skrill-dark{background-image:url(/static/media/skrill-dark.a1a4a38c.svg)}.payment-skrill{background-image:url(/static/media/skrill.b0d31271.svg)}.payment-solo-dark{background-image:url(/static/media/solo-dark.17da28b9.svg)}.payment-solo{background-image:url(/static/media/solo.f7fcc525.svg)}.payment-square-dark{background-image:url(/static/media/square-dark.4db9c83c.svg)}.payment-square{background-image:url(/static/media/square.48f11398.svg)}.payment-stripe-dark{background-image:url(/static/media/stripe-dark.025afc35.svg)}.payment-stripe{background-image:url(/static/media/stripe.77c6af28.svg)}.payment-switch-dark{background-image:url(/static/media/switch-dark.54599ad9.svg)}.payment-switch{background-image:url(/static/media/switch.c1a0e47d.svg)}.payment-ukash-dark{background-image:url(/static/media/ukash-dark.89b7d2ae.svg)}.payment-ukash{background-image:url(/static/media/ukash.7a542b9e.svg)}.payment-unionpay-dark{background-image:url(/static/media/unionpay-dark.22beb1a2.svg)}.payment-unionpay{background-image:url(/static/media/unionpay.285de38e.svg)}.payment-verifone-dark{background-image:url(/static/media/verifone-dark.e7b2a0bc.svg)}.payment-verifone{background-image:url(/static/media/verifone.012caff4.svg)}.payment-verisign-dark{background-image:url(/static/media/verisign-dark.1f0c2c56.svg)}.payment-verisign{background-image:url(/static/media/verisign.3684cf82.svg)}.payment-visa-dark{background-image:url(/static/media/visa-dark.f6a55e1d.svg)}.payment-visa{background-image:url(/static/media/visa.a09152e7.svg)}.payment-webmoney-dark{background-image:url(/static/media/webmoney-dark.5c559c4c.svg)}.payment-webmoney{background-image:url(/static/media/webmoney.c77724f3.svg)}.payment-westernunion-dark{background-image:url(/static/media/westernunion-dark.5f3974a3.svg)}.payment-westernunion{background-image:url(/static/media/westernunion.4082e1b1.svg)}.payment-worldpay-dark{background-image:url(/static/media/worldpay-dark.a99e6d1c.svg)}.payment-worldpay{background-image:url(/static/media/worldpay.d63620a3.svg)}svg{touch-action:none}.jvectormap-container{width:100%;height:100%;position:relative;overflow:hidden;touch-action:none}.jvectormap-tip{position:absolute;display:none;border-radius:3px;background:#212529;color:#fff;padding:6px;font-size:11px;line-height:1;font-weight:700}.jvectormap-tip small{font-size:inherit;font-weight:400}.jvectormap-goback,.jvectormap-zoomin,.jvectormap-zoomout{position:absolute;left:10px;border-radius:3px;background:#292929;padding:3px;color:#fff;cursor:pointer;line-height:10px;text-align:center;box-sizing:initial}.jvectormap-zoomin,.jvectormap-zoomout{width:10px;height:10px}.jvectormap-zoomin{top:10px}.jvectormap-zoomout{top:30px}.jvectormap-goback{bottom:10px;z-index:1000;padding:6px}.jvectormap-spinner{position:absolute;left:0;top:0;right:0;bottom:0;background:url(data:image/gif;base64,R0lGODlhIAAgAPMAAP///wAAAMbGxoSEhLa2tpqamjY2NlZWVtjY2OTk5Ly8vB4eHgQEBAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAIAAgAAAE5xDISWlhperN52JLhSSdRgwVo1ICQZRUsiwHpTJT4iowNS8vyW2icCF6k8HMMBkCEDskxTBDAZwuAkkqIfxIQyhBQBFvAQSDITM5VDW6XNE4KagNh6Bgwe60smQUB3d4Rz1ZBApnFASDd0hihh12BkE9kjAJVlycXIg7CQIFA6SlnJ87paqbSKiKoqusnbMdmDC2tXQlkUhziYtyWTxIfy6BE8WJt5YJvpJivxNaGmLHT0VnOgSYf0dZXS7APdpB309RnHOG5gDqXGLDaC457D1zZ/V/nmOM82XiHRLYKhKP1oZmADdEAAAh+QQJCgAAACwAAAAAIAAgAAAE6hDISWlZpOrNp1lGNRSdRpDUolIGw5RUYhhHukqFu8DsrEyqnWThGvAmhVlteBvojpTDDBUEIFwMFBRAmBkSgOrBFZogCASwBDEY/CZSg7GSE0gSCjQBMVG023xWBhklAnoEdhQEfyNqMIcKjhRsjEdnezB+A4k8gTwJhFuiW4dokXiloUepBAp5qaKpp6+Ho7aWW54wl7obvEe0kRuoplCGepwSx2jJvqHEmGt6whJpGpfJCHmOoNHKaHx61WiSR92E4lbFoq+B6QDtuetcaBPnW6+O7wDHpIiK9SaVK5GgV543tzjgGcghAgAh+QQJCgAAACwAAAAAIAAgAAAE7hDISSkxpOrN5zFHNWRdhSiVoVLHspRUMoyUakyEe8PTPCATW9A14E0UvuAKMNAZKYUZCiBMuBakSQKG8G2FzUWox2AUtAQFcBKlVQoLgQReZhQlCIJesQXI5B0CBnUMOxMCenoCfTCEWBsJColTMANldx15BGs8B5wlCZ9Po6OJkwmRpnqkqnuSrayqfKmqpLajoiW5HJq7FL1Gr2mMMcKUMIiJgIemy7xZtJsTmsM4xHiKv5KMCXqfyUCJEonXPN2rAOIAmsfB3uPoAK++G+w48edZPK+M6hLJpQg484enXIdQFSS1u6UhksENEQAAIfkECQoAAAAsAAAAACAAIAAABOcQyEmpGKLqzWcZRVUQnZYg1aBSh2GUVEIQ2aQOE+G+cD4ntpWkZQj1JIiZIogDFFyHI0UxQwFugMSOFIPJftfVAEoZLBbcLEFhlQiqGp1Vd140AUklUN3eCA51C1EWMzMCezCBBmkxVIVHBWd3HHl9JQOIJSdSnJ0TDKChCwUJjoWMPaGqDKannasMo6WnM562R5YluZRwur0wpgqZE7NKUm+FNRPIhjBJxKZteWuIBMN4zRMIVIhffcgojwCF117i4nlLnY5ztRLsnOk+aV+oJY7V7m76PdkS4trKcdg0Zc0tTcKkRAAAIfkECQoAAAAsAAAAACAAIAAABO4QyEkpKqjqzScpRaVkXZWQEximw1BSCUEIlDohrft6cpKCk5xid5MNJTaAIkekKGQkWyKHkvhKsR7ARmitkAYDYRIbUQRQjWBwJRzChi9CRlBcY1UN4g0/VNB0AlcvcAYHRyZPdEQFYV8ccwR5HWxEJ02YmRMLnJ1xCYp0Y5idpQuhopmmC2KgojKasUQDk5BNAwwMOh2RtRq5uQuPZKGIJQIGwAwGf6I0JXMpC8C7kXWDBINFMxS4DKMAWVWAGYsAdNqW5uaRxkSKJOZKaU3tPOBZ4DuK2LATgJhkPJMgTwKCdFjyPHEnKxFCDhEAACH5BAkKAAAALAAAAAAgACAAAATzEMhJaVKp6s2nIkolIJ2WkBShpkVRWqqQrhLSEu9MZJKK9y1ZrqYK9WiClmvoUaF8gIQSNeF1Er4MNFn4SRSDARWroAIETg1iVwuHjYB1kYc1mwruwXKC9gmsJXliGxc+XiUCby9ydh1sOSdMkpMTBpaXBzsfhoc5l58Gm5yToAaZhaOUqjkDgCWNHAULCwOLaTmzswadEqggQwgHuQsHIoZCHQMMQgQGubVEcxOPFAcMDAYUA85eWARmfSRQCdcMe0zeP1AAygwLlJtPNAAL19DARdPzBOWSm1brJBi45soRAWQAAkrQIykShQ9wVhHCwCQCACH5BAkKAAAALAAAAAAgACAAAATrEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq+E71SRQeyqUToLA7VxF0JDyIQh/MVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiRMDjI0Fd30/iI2UA5GSS5UDj2l6NoqgOgN4gksEBgYFf0FDqKgHnyZ9OX8HrgYHdHpcHQULXAS2qKpENRg7eAMLC7kTBaixUYFkKAzWAAnLC7FLVxLWDBLKCwaKTULgEwbLA4hJtOkSBNqITT3xEgfLpBtzE/jiuL04RGEBgwWhShRgQExHBAAh+QQJCgAAACwAAAAAIAAgAAAE7xDISWlSqerNpyJKhWRdlSAVoVLCWk6JKlAqAavhO9UkUHsqlE6CwO1cRdCQ8iEIfzFVTzLdRAmZX3I2SfZiCqGk5dTESJeaOAlClzsJsqwiJwiqnFrb2nS9kmIcgEsjQydLiIlHehhpejaIjzh9eomSjZR+ipslWIRLAgMDOR2DOqKogTB9pCUJBagDBXR6XB0EBkIIsaRsGGMMAxoDBgYHTKJiUYEGDAzHC9EACcUGkIgFzgwZ0QsSBcXHiQvOwgDdEwfFs0sDzt4S6BK4xYjkDOzn0unFeBzOBijIm1Dgmg5YFQwsCMjp1oJ8LyIAACH5BAkKAAAALAAAAAAgACAAAATwEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq+E71SRQeyqUToLA7VxF0JDyIQh/MVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiUd6GGl6NoiPOH16iZKNlH6KmyWFOggHhEEvAwwMA0N9GBsEC6amhnVcEwavDAazGwIDaH1ipaYLBUTCGgQDA8NdHz0FpqgTBwsLqAbWAAnIA4FWKdMLGdYGEgraigbT0OITBcg5QwPT4xLrROZL6AuQAPUS7bxLpoWidY0JtxLHKhwwMJBTHgPKdEQAACH5BAkKAAAALAAAAAAgACAAAATrEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq+E71SRQeyqUToLA7VxF0JDyIQh/MVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiUd6GAULDJCRiXo1CpGXDJOUjY+Yip9DhToJA4RBLwMLCwVDfRgbBAaqqoZ1XBMHswsHtxtFaH1iqaoGNgAIxRpbFAgfPQSqpbgGBqUD1wBXeCYp1AYZ19JJOYgH1KwA4UBvQwXUBxPqVD9L3sbp2BNk2xvvFPJd+MFCN6HAAIKgNggY0KtEBAAh+QQJCgAAACwAAAAAIAAgAAAE6BDISWlSqerNpyJKhWRdlSAVoVLCWk6JKlAqAavhO9UkUHsqlE6CwO1cRdCQ8iEIfzFVTzLdRAmZX3I2SfYIDMaAFdTESJeaEDAIMxYFqrOUaNW4E4ObYcCXaiBVEgULe0NJaxxtYksjh2NLkZISgDgJhHthkpU4mW6blRiYmZOlh4JWkDqILwUGBnE6TYEbCgevr0N1gH4At7gHiRpFaLNrrq8HNgAJA70AWxQIH1+vsYMDAzZQPC9VCNkDWUhGkuE5PxJNwiUK4UfLzOlD4WvzAHaoG9nxPi5d+jYUqfAhhykOFwJWiAAAIfkECQoAAAAsAAAAACAAIAAABPAQyElpUqnqzaciSoVkXVUMFaFSwlpOCcMYlErAavhOMnNLNo8KsZsMZItJEIDIFSkLGQoQTNhIsFehRww2CQLKF0tYGKYSg+ygsZIuNqJksKgbfgIGepNo2cIUB3V1B3IvNiBYNQaDSTtfhhx0CwVPI0UJe0+bm4g5VgcGoqOcnjmjqDSdnhgEoamcsZuXO1aWQy8KAwOAuTYYGwi7w5h+Kr0SJ8MFihpNbx+4Erq7BYBuzsdiH1jCAzoSfl0rVirNbRXlBBlLX+BP0XJLAPGzTkAuAOqb0WT5AH7OcdCm5B8TgRwSRKIHQtaLCwg1RAAAOwAAAAAAAAAAAA==)}.jvectormap-legend-title{font-weight:700;font-size:14px;text-align:center}.jvectormap-legend-cnt{position:absolute}.jvectormap-legend-cnt-h{bottom:0;right:0}.jvectormap-legend-cnt-v{top:0;right:0}.jvectormap-legend{background:#000;color:#fff;border-radius:3px}.jvectormap-legend-cnt-h .jvectormap-legend{float:left;margin:0 10px 10px 0;padding:3px 3px 1px}.jvectormap-legend-cnt-h .jvectormap-legend .jvectormap-legend-tick{float:left}.jvectormap-legend-cnt-v .jvectormap-legend{margin:10px 10px 0 0;padding:3px}.jvectormap-legend-cnt-h .jvectormap-legend-tick{width:40px}.jvectormap-legend-cnt-h .jvectormap-legend-tick-sample{height:15px}.jvectormap-legend-cnt-v .jvectormap-legend-tick-sample{height:20px;width:20px;display:inline-block;vertical-align:middle}.jvectormap-legend-tick-text{font-size:12px}.jvectormap-legend-cnt-h .jvectormap-legend-tick-text{text-align:center}.jvectormap-legend-cnt-v .jvectormap-legend-tick-text{display:inline-block;vertical-align:middle;line-height:20px;padding-left:3px}.selectize-control.plugin-drag_drop.multi>.selectize-input>div.ui-sortable-placeholder{visibility:visible!important;background:#f2f2f2!important;background:rgba(0,0,0,.06)!important;border:0!important;box-shadow:inset 0 0 12px 4px #fff}.selectize-control.plugin-drag_drop .ui-sortable-placeholder:after{content:"!";visibility:hidden}.selectize-control.plugin-drag_drop .ui-sortable-helper{box-shadow:0 2px 5px rgba(0,0,0,.2)}.selectize-dropdown-header{position:relative;padding:5px 8px;border-bottom:1px solid #d0d0d0;background:#f8f8f8;border-radius:3px 3px 0 0}.selectize-dropdown-header-close{position:absolute;right:8px;top:50%;color:#495057;opacity:.4;margin-top:-12px;line-height:20px;font-size:20px!important}.selectize-dropdown-header-close:hover{color:#000}.selectize-dropdown.plugin-optgroup_columns .optgroup{border-right:1px solid #f2f2f2;border-top:0;float:left;box-sizing:border-box}.selectize-dropdown.plugin-optgroup_columns .optgroup:last-child{border-right:0}.selectize-dropdown.plugin-optgroup_columns .optgroup:before{display:none}.selectize-dropdown.plugin-optgroup_columns .optgroup-header{border-top:0}.selectize-control.plugin-remove_button [data-value]{position:relative;padding-right:24px!important}.selectize-control.plugin-remove_button [data-value] .remove{z-index:1;position:absolute;top:0;right:0;bottom:0;width:17px;text-align:center;font-weight:700;font-size:12px;color:inherit;text-decoration:none;vertical-align:middle;display:inline-block;padding:2px 0 0;border-left:1px solid #d0d0d0;border-radius:0 2px 2px 0;box-sizing:border-box}.selectize-control.plugin-remove_button [data-value] .remove:hover{background:rgba(0,0,0,.05)}.selectize-control.plugin-remove_button [data-value].active .remove{border-left-color:#cacaca}.selectize-control.plugin-remove_button .disabled [data-value] .remove:hover{background:none}.selectize-control.plugin-remove_button .disabled [data-value] .remove{border-left-color:#fff}.selectize-control.plugin-remove_button .remove-single{position:absolute;right:28px;top:6px;font-size:23px}.selectize-control{position:relative;padding:0;border:0}.selectize-dropdown,.selectize-input,.selectize-input input{color:#495057;font-family:inherit;font-size:15px;line-height:18px;-webkit-font-smoothing:inherit}.selectize-control.single .selectize-input.input-active,.selectize-input{background:#fff;cursor:text;display:inline-block}.selectize-input{border:1px solid rgba(0,40,100,.12);padding:.5625rem .75rem;display:inline-block;display:block;width:100%;overflow:hidden;position:relative;z-index:1;box-sizing:border-box;border-radius:3px;transition:border-color .3s,box-shadow .3s}.selectize-control.multi .selectize-input.has-items{padding:7px .75rem 4px 7px}.selectize-input.full{background-color:#fff}.selectize-input.disabled,.selectize-input.disabled *{cursor:default!important}.selectize-input.focus{border-color:#467fcf;box-shadow:0 0 0 2px rgba(70,127,207,.25)}.selectize-input.dropdown-active{border-radius:3px 3px 0 0}.selectize-input>*{vertical-align:initial;display:-moz-inline-stack;display:inline-block;zoom:1;*display:inline}.selectize-control.multi .selectize-input>div{cursor:pointer;margin:0 3px 3px 0;padding:2px 6px;background:#e9ecef;color:#495057;font-size:13px;border:0 solid rgba(0,40,100,.12);border-radius:3px;font-weight:400}.selectize-control.multi .selectize-input>div.active{background:#e8e8e8;color:#303030;border:0 solid #cacaca}.selectize-control.multi .selectize-input.disabled>div,.selectize-control.multi .selectize-input.disabled>div.active{color:#7d7d7d;background:#fff;border:0 solid #fff}.selectize-input>input{display:inline-block!important;padding:0!important;min-height:0!important;max-height:none!important;max-width:100%!important;margin:0 2px 0 0!important;text-indent:0!important;border:0!important;background:none!important;line-height:inherit!important;box-shadow:none!important}.selectize-input>input::-ms-clear{display:none}.selectize-input>input:focus{outline:none!important}.selectize-input:after{content:" ";display:block;clear:left}.selectize-input.dropdown-active:before{content:" ";display:block;position:absolute;background:#f0f0f0;height:1px;bottom:0;left:0;right:0}.selectize-dropdown{position:absolute;z-index:10;background:#fff;margin:-1px 0 0;border:1px solid rgba(0,40,100,.12);border-top:0;box-sizing:border-box;border-radius:0 0 3px 3px;height:auto;padding:0}.selectize-dropdown [data-selectable]{cursor:pointer;overflow:hidden}.selectize-dropdown [data-selectable] .highlight{background:rgba(125,168,208,.2);border-radius:1px}.selectize-dropdown .optgroup-header,.selectize-dropdown [data-selectable]{padding:6px .75rem}.selectize-dropdown .optgroup:first-child .optgroup-header{border-top:0}.selectize-dropdown .optgroup-header{color:#495057;background:#fff;cursor:default}.selectize-dropdown .active{background-color:#f1f4f8;color:#467fcf}.selectize-dropdown .active.create{color:#495057}.selectize-dropdown .create{color:rgba(48,48,48,.5)}.selectize-dropdown-content{overflow-y:auto;overflow-x:hidden;max-height:200px;-webkit-overflow-scrolling:touch}.selectize-control.single .selectize-input,.selectize-control.single .selectize-input input{cursor:pointer}.selectize-control.single .selectize-input.input-active,.selectize-control.single .selectize-input.input-active input{cursor:text}.selectize-control.single .selectize-input:after{content:"";display:block;position:absolute;top:13px;right:12px;width:8px;height:10px;background:#fff url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 5'%3E%3Cpath fill='#999' d='M0 0L10 0L5 5L0 0'/%3E%3C/svg%3E") no-repeat 50%;background-size:8px 10px;transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s}.selectize-control.single .selectize-input.dropdown-active:after{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.selectize-control .selectize-input.disabled{opacity:.5;background-color:#fafafa}.selectize-dropdown .image,.selectize-input .image{width:1.25rem;height:1.25rem;background-size:contain;margin:-1px .5rem -1px -4px;line-height:1.25rem;float:left;display:flex;align-items:center;justify-content:center}.selectize-dropdown .image img,.selectize-input .image img{max-width:100%;box-shadow:0 1px 2px 0 rgba(0,0,0,.4);border-radius:2px}.selectize-input .image{width:1.5rem;height:1.5rem;margin:-3px .75rem -3px -5px}@font-face{font-family:feather;src:url(/static/media/feather-webfont.cc5143b2.eot);src:url(/static/media/feather-webfont.cc5143b2.eot#iefix) format("embedded-opentype"),url(/static/media/feather-webfont.2cf523cd.woff) format("woff"),url(/static/media/feather-webfont.b8e9cbc7.ttf) format("truetype"),url(/static/media/feather-webfont.4a878d5b.svg#feather) format("svg")}.fe{font-family:feather!important;speak:none;font-style:normal;font-weight:400;-webkit-font-feature-settings:normal;font-feature-settings:normal;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fe-activity:before{content:"\E900"}.fe-airplay:before{content:"\E901"}.fe-alert-circle:before{content:"\E902"}.fe-alert-octagon:before{content:"\E903"}.fe-alert-triangle:before{content:"\E904"}.fe-align-center:before{content:"\E905"}.fe-align-justify:before{content:"\E906"}.fe-align-left:before{content:"\E907"}.fe-align-right:before{content:"\E908"}.fe-anchor:before{content:"\E909"}.fe-aperture:before{content:"\E90A"}.fe-arrow-down:before{content:"\E90B"}.fe-arrow-down-circle:before{content:"\E90C"}.fe-arrow-down-left:before{content:"\E90D"}.fe-arrow-down-right:before{content:"\E90E"}.fe-arrow-left:before{content:"\E90F"}.fe-arrow-left-circle:before{content:"\E910"}.fe-arrow-right:before{content:"\E911"}.fe-arrow-right-circle:before{content:"\E912"}.fe-arrow-up:before{content:"\E913"}.fe-arrow-up-circle:before{content:"\E914"}.fe-arrow-up-left:before{content:"\E915"}.fe-arrow-up-right:before{content:"\E916"}.fe-at-sign:before{content:"\E917"}.fe-award:before{content:"\E918"}.fe-bar-chart:before{content:"\E919"}.fe-bar-chart-2:before{content:"\E91A"}.fe-battery:before{content:"\E91B"}.fe-battery-charging:before{content:"\E91C"}.fe-bell:before{content:"\E91D"}.fe-bell-off:before{content:"\E91E"}.fe-bluetooth:before{content:"\E91F"}.fe-bold:before{content:"\E920"}.fe-book:before{content:"\E921"}.fe-book-open:before{content:"\E922"}.fe-bookmark:before{content:"\E923"}.fe-box:before{content:"\E924"}.fe-briefcase:before{content:"\E925"}.fe-calendar:before{content:"\E926"}.fe-camera:before{content:"\E927"}.fe-camera-off:before{content:"\E928"}.fe-cast:before{content:"\E929"}.fe-check:before{content:"\E92A"}.fe-check-circle:before{content:"\E92B"}.fe-check-square:before{content:"\E92C"}.fe-chevron-down:before{content:"\E92D"}.fe-chevron-left:before{content:"\E92E"}.fe-chevron-right:before{content:"\E92F"}.fe-chevron-up:before{content:"\E930"}.fe-chevrons-down:before{content:"\E931"}.fe-chevrons-left:before{content:"\E932"}.fe-chevrons-right:before{content:"\E933"}.fe-chevrons-up:before{content:"\E934"}.fe-chrome:before{content:"\E935"}.fe-circle:before{content:"\E936"}.fe-clipboard:before{content:"\E937"}.fe-clock:before{content:"\E938"}.fe-cloud:before{content:"\E939"}.fe-cloud-drizzle:before{content:"\E93A"}.fe-cloud-lightning:before{content:"\E93B"}.fe-cloud-off:before{content:"\E93C"}.fe-cloud-rain:before{content:"\E93D"}.fe-cloud-snow:before{content:"\E93E"}.fe-code:before{content:"\E93F"}.fe-codepen:before{content:"\E940"}.fe-command:before{content:"\E941"}.fe-compass:before{content:"\E942"}.fe-copy:before{content:"\E943"}.fe-corner-down-left:before{content:"\E944"}.fe-corner-down-right:before{content:"\E945"}.fe-corner-left-down:before{content:"\E946"}.fe-corner-left-up:before{content:"\E947"}.fe-corner-right-down:before{content:"\E948"}.fe-corner-right-up:before{content:"\E949"}.fe-corner-up-left:before{content:"\E94A"}.fe-corner-up-right:before{content:"\E94B"}.fe-cpu:before{content:"\E94C"}.fe-credit-card:before{content:"\E94D"}.fe-crop:before{content:"\E94E"}.fe-crosshair:before{content:"\E94F"}.fe-database:before{content:"\E950"}.fe-delete:before{content:"\E951"}.fe-disc:before{content:"\E952"}.fe-dollar-sign:before{content:"\E953"}.fe-download:before{content:"\E954"}.fe-download-cloud:before{content:"\E955"}.fe-droplet:before{content:"\E956"}.fe-edit:before{content:"\E957"}.fe-edit-2:before{content:"\E958"}.fe-edit-3:before{content:"\E959"}.fe-external-link:before{content:"\E95A"}.fe-eye:before{content:"\E95B"}.fe-eye-off:before{content:"\E95C"}.fe-facebook:before{content:"\E95D"}.fe-fast-forward:before{content:"\E95E"}.fe-feather:before{content:"\E95F"}.fe-file:before{content:"\E960"}.fe-file-minus:before{content:"\E961"}.fe-file-plus:before{content:"\E962"}.fe-file-text:before{content:"\E963"}.fe-film:before{content:"\E964"}.fe-filter:before{content:"\E965"}.fe-flag:before{content:"\E966"}.fe-folder:before{content:"\E967"}.fe-folder-minus:before{content:"\E968"}.fe-folder-plus:before{content:"\E969"}.fe-git-branch:before{content:"\E96A"}.fe-git-commit:before{content:"\E96B"}.fe-git-merge:before{content:"\E96C"}.fe-git-pull-request:before{content:"\E96D"}.fe-github:before{content:"\E96E"}.fe-gitlab:before{content:"\E96F"}.fe-globe:before{content:"\E970"}.fe-grid:before{content:"\E971"}.fe-hard-drive:before{content:"\E972"}.fe-hash:before{content:"\E973"}.fe-headphones:before{content:"\E974"}.fe-heart:before{content:"\E975"}.fe-help-circle:before{content:"\E976"}.fe-home:before{content:"\E977"}.fe-image:before{content:"\E978"}.fe-inbox:before{content:"\E979"}.fe-info:before{content:"\E97A"}.fe-instagram:before{content:"\E97B"}.fe-italic:before{content:"\E97C"}.fe-layers:before{content:"\E97D"}.fe-layout:before{content:"\E97E"}.fe-life-buoy:before{content:"\E97F"}.fe-link:before{content:"\E980"}.fe-link-2:before{content:"\E981"}.fe-linkedin:before{content:"\E982"}.fe-list:before{content:"\E983"}.fe-loader:before{content:"\E984"}.fe-lock:before{content:"\E985"}.fe-log-in:before{content:"\E986"}.fe-log-out:before{content:"\E987"}.fe-mail:before{content:"\E988"}.fe-map:before{content:"\E989"}.fe-map-pin:before{content:"\E98A"}.fe-maximize:before{content:"\E98B"}.fe-maximize-2:before{content:"\E98C"}.fe-menu:before{content:"\E98D"}.fe-message-circle:before{content:"\E98E"}.fe-message-square:before{content:"\E98F"}.fe-mic:before{content:"\E990"}.fe-mic-off:before{content:"\E991"}.fe-minimize:before{content:"\E992"}.fe-minimize-2:before{content:"\E993"}.fe-minus:before{content:"\E994"}.fe-minus-circle:before{content:"\E995"}.fe-minus-square:before{content:"\E996"}.fe-monitor:before{content:"\E997"}.fe-moon:before{content:"\E998"}.fe-more-horizontal:before{content:"\E999"}.fe-more-vertical:before{content:"\E99A"}.fe-move:before{content:"\E99B"}.fe-music:before{content:"\E99C"}.fe-navigation:before{content:"\E99D"}.fe-navigation-2:before{content:"\E99E"}.fe-octagon:before{content:"\E99F"}.fe-package:before{content:"\E9A0"}.fe-paperclip:before{content:"\E9A1"}.fe-pause:before{content:"\E9A2"}.fe-pause-circle:before{content:"\E9A3"}.fe-percent:before{content:"\E9A4"}.fe-phone:before{content:"\E9A5"}.fe-phone-call:before{content:"\E9A6"}.fe-phone-forwarded:before{content:"\E9A7"}.fe-phone-incoming:before{content:"\E9A8"}.fe-phone-missed:before{content:"\E9A9"}.fe-phone-off:before{content:"\E9AA"}.fe-phone-outgoing:before{content:"\E9AB"}.fe-pie-chart:before{content:"\E9AC"}.fe-play:before{content:"\E9AD"}.fe-play-circle:before{content:"\E9AE"}.fe-plus:before{content:"\E9AF"}.fe-plus-circle:before{content:"\E9B0"}.fe-plus-square:before{content:"\E9B1"}.fe-pocket:before{content:"\E9B2"}.fe-power:before{content:"\E9B3"}.fe-printer:before{content:"\E9B4"}.fe-radio:before{content:"\E9B5"}.fe-refresh-ccw:before{content:"\E9B6"}.fe-refresh-cw:before{content:"\E9B7"}.fe-repeat:before{content:"\E9B8"}.fe-rewind:before{content:"\E9B9"}.fe-rotate-ccw:before{content:"\E9BA"}.fe-rotate-cw:before{content:"\E9BB"}.fe-rss:before{content:"\E9BC"}.fe-save:before{content:"\E9BD"}.fe-scissors:before{content:"\E9BE"}.fe-search:before{content:"\E9BF"}.fe-send:before{content:"\E9C0"}.fe-server:before{content:"\E9C1"}.fe-settings:before{content:"\E9C2"}.fe-share:before{content:"\E9C3"}.fe-share-2:before{content:"\E9C4"}.fe-shield:before{content:"\E9C5"}.fe-shield-off:before{content:"\E9C6"}.fe-shopping-bag:before{content:"\E9C7"}.fe-shopping-cart:before{content:"\E9C8"}.fe-shuffle:before{content:"\E9C9"}.fe-sidebar:before{content:"\E9CA"}.fe-skip-back:before{content:"\E9CB"}.fe-skip-forward:before{content:"\E9CC"}.fe-slack:before{content:"\E9CD"}.fe-slash:before{content:"\E9CE"}.fe-sliders:before{content:"\E9CF"}.fe-smartphone:before{content:"\E9D0"}.fe-speaker:before{content:"\E9D1"}.fe-square:before{content:"\E9D2"}.fe-star:before{content:"\E9D3"}.fe-stop-circle:before{content:"\E9D4"}.fe-sun:before{content:"\E9D5"}.fe-sunrise:before{content:"\E9D6"}.fe-sunset:before{content:"\E9D7"}.fe-tablet:before{content:"\E9D8"}.fe-tag:before{content:"\E9D9"}.fe-target:before{content:"\E9DA"}.fe-terminal:before{content:"\E9DB"}.fe-thermometer:before{content:"\E9DC"}.fe-thumbs-down:before{content:"\E9DD"}.fe-thumbs-up:before{content:"\E9DE"}.fe-toggle-left:before{content:"\E9DF"}.fe-toggle-right:before{content:"\E9E0"}.fe-trash:before{content:"\E9E1"}.fe-trash-2:before{content:"\E9E2"}.fe-trending-down:before{content:"\E9E3"}.fe-trending-up:before{content:"\E9E4"}.fe-triangle:before{content:"\E9E5"}.fe-truck:before{content:"\E9E6"}.fe-tv:before{content:"\E9E7"}.fe-twitter:before{content:"\E9E8"}.fe-type:before{content:"\E9E9"}.fe-umbrella:before{content:"\E9EA"}.fe-underline:before{content:"\E9EB"}.fe-unlock:before{content:"\E9EC"}.fe-upload:before{content:"\E9ED"}.fe-upload-cloud:before{content:"\E9EE"}.fe-user:before{content:"\E9EF"}.fe-user-check:before{content:"\E9F0"}.fe-user-minus:before{content:"\E9F1"}.fe-user-plus:before{content:"\E9F2"}.fe-user-x:before{content:"\E9F3"}.fe-users:before{content:"\E9F4"}.fe-video:before{content:"\E9F5"}.fe-video-off:before{content:"\E9F6"}.fe-voicemail:before{content:"\E9F7"}.fe-volume:before{content:"\E9F8"}.fe-volume-1:before{content:"\E9F9"}.fe-volume-2:before{content:"\E9FA"}.fe-volume-x:before{content:"\E9FB"}.fe-watch:before{content:"\E9FC"}.fe-wifi:before{content:"\E9FD"}.fe-wifi-off:before{content:"\E9FE"}.fe-wind:before{content:"\E9FF"}.fe-x:before{content:"\EA00"}.fe-x-circle:before{content:"\EA01"}.fe-x-square:before{content:"\EA02"}.fe-zap:before{content:"\EA03"}.fe-zap-off:before{content:"\EA04"}.fe-zoom-in:before{content:"\EA05"}.fe-zoom-out:before{content:"\EA06"} -/*# sourceMappingURL=1.13813cb9.chunk.css.map */ \ No newline at end of file diff --git a/onvm_web/web-build/static/css/1.fe79427a.chunk.css b/onvm_web/web-build/static/css/1.fe79427a.chunk.css new file mode 100644 index 000000000..6b939c5f0 --- /dev/null +++ b/onvm_web/web-build/static/css/1.fe79427a.chunk.css @@ -0,0 +1,6 @@ +/*! + * Bootstrap v4.0.0 (https://getbootstrap.com) + * Copyright 2011-2018 The Bootstrap Authors + * Copyright 2011-2018 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */:root{--blue:#467fcf;--indigo:#6574cd;--purple:#a55eea;--pink:#f66d9b;--red:#cd201f;--orange:#fd9644;--yellow:#f1c40f;--green:#5eba00;--teal:#2bcbba;--cyan:#17a2b8;--white:#fff;--gray:#868e96;--gray-dark:#343a40;--azure:#45aaf2;--lime:#7bd235;--primary:#467fcf;--secondary:#868e96;--success:#5eba00;--info:#45aaf2;--warning:#f1c40f;--danger:#cd201f;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1280px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";--font-family-monospace:Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,:after,:before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:transparent}@-ms-viewport{width:device-width}article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:Source Sans Pro,-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica Neue,Arial,sans-serif;font-size:.9375rem;font-weight:400;line-height:1.5;color:#495057;text-align:left;background-color:#f5f7fb}[tabindex="-1"]:focus{outline:0 !important}hr{box-sizing:initial;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.66em}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0}address{font-style:normal;line-height:inherit}address,dl,ol,ul{margin-bottom:1rem}dl,ol,ul{margin-top:0}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}dfn{font-style:italic}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}a{color:#467fcf;text-decoration:none;background-color:initial;-webkit-text-decoration-skip:objects}a:hover{color:#295a9f;text-decoration:underline}a:not([href]):not([tabindex]),a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg:not(:root){overflow:hidden}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#9aa0ac;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:initial}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none !important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.66em;font-family:inherit;font-weight:600;line-height:1.1;color:inherit}.h1,h1{font-size:2rem}.h2,h2{font-size:1.75rem}.h3,h3{font-size:1.5rem}.h4,h4{font-size:1.125rem}.h5,h5{font-size:1rem}.h6,h6{font-size:.875rem}.lead{font-size:1.171875rem;font-weight:300}.display-1{font-size:4.5rem}.display-1,.display-2{font-weight:300;line-height:1.1}.display-2{font-size:4rem}.display-3{font-size:3.5rem}.display-3,.display-4{font-weight:300;line-height:1.1}.display-4{font-size:3rem}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,40,100,.12)}.small,small{font-size:87.5%;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-inline,.list-unstyled{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.171875rem}.blockquote-footer{display:block;font-size:80%;color:#868e96}.blockquote-footer:before{content:"\2014 \A0"}.img-fluid,.img-thumbnail{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:3px}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#868e96}code,kbd,pre,samp{font-family:Monaco,Consolas,Liberation Mono,Courier New,monospace}code{font-size:85%;word-break:break-word}a>code,code{color:inherit}kbd{padding:.2rem .4rem;font-size:85%;color:#fff;background-color:#343a40;border-radius:3px}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;color:#212529}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{width:100%;padding-right:.75rem;padding-left:.75rem;margin-right:auto;margin-left:auto}@media(min-width:576px){.container{max-width:540px}}@media(min-width:768px){.container{max-width:720px}}@media(min-width:992px){.container{max-width:960px}}@media(min-width:1280px){.container{max-width:1200px}}.container-fluid{width:100%;padding-right:.75rem;padding-left:.75rem;margin-right:auto;margin-left:auto}.row{display:flex;flex-wrap:wrap;margin-right:-.75rem;margin-left:-.75rem}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-10,.col-11,.col-12,.col-auto,.col-lg,.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-auto,.col-md,.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12,.col-md-auto,.col-sm,.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-auto{position:relative;width:100%;min-height:1px;padding-right:.75rem;padding-left:.75rem}.col{flex-basis:0;flex-grow:1;max-width:100%}.col-auto{flex:0 0 auto;width:auto;max-width:none}.col-1{flex:0 0 8.33333333%;max-width:8.33333333%}.col-2{flex:0 0 16.66666667%;max-width:16.66666667%}.col-3{flex:0 0 25%;max-width:25%}.col-4{flex:0 0 33.33333333%;max-width:33.33333333%}.col-5{flex:0 0 41.66666667%;max-width:41.66666667%}.col-6{flex:0 0 50%;max-width:50%}.col-7{flex:0 0 58.33333333%;max-width:58.33333333%}.col-8{flex:0 0 66.66666667%;max-width:66.66666667%}.col-9{flex:0 0 75%;max-width:75%}.col-10{flex:0 0 83.33333333%;max-width:83.33333333%}.col-11{flex:0 0 91.66666667%;max-width:91.66666667%}.col-12{flex:0 0 100%;max-width:100%}.order-first{order:-1}.order-last{order:13}.order-0{order:0}.order-1{order:1}.order-2{order:2}.order-3{order:3}.order-4{order:4}.order-5{order:5}.order-6{order:6}.order-7{order:7}.order-8{order:8}.order-9{order:9}.order-10{order:10}.order-11{order:11}.order-12{order:12}.offset-1{margin-left:8.33333333%}.offset-2{margin-left:16.66666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.33333333%}.offset-5{margin-left:41.66666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.33333333%}.offset-8{margin-left:66.66666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.33333333%}.offset-11{margin-left:91.66666667%}@media(min-width:576px){.col-sm{flex-basis:0;flex-grow:1;max-width:100%}.col-sm-auto{flex:0 0 auto;width:auto;max-width:none}.col-sm-1{flex:0 0 8.33333333%;max-width:8.33333333%}.col-sm-2{flex:0 0 16.66666667%;max-width:16.66666667%}.col-sm-3{flex:0 0 25%;max-width:25%}.col-sm-4{flex:0 0 33.33333333%;max-width:33.33333333%}.col-sm-5{flex:0 0 41.66666667%;max-width:41.66666667%}.col-sm-6{flex:0 0 50%;max-width:50%}.col-sm-7{flex:0 0 58.33333333%;max-width:58.33333333%}.col-sm-8{flex:0 0 66.66666667%;max-width:66.66666667%}.col-sm-9{flex:0 0 75%;max-width:75%}.col-sm-10{flex:0 0 83.33333333%;max-width:83.33333333%}.col-sm-11{flex:0 0 91.66666667%;max-width:91.66666667%}.col-sm-12{flex:0 0 100%;max-width:100%}.order-sm-first{order:-1}.order-sm-last{order:13}.order-sm-0{order:0}.order-sm-1{order:1}.order-sm-2{order:2}.order-sm-3{order:3}.order-sm-4{order:4}.order-sm-5{order:5}.order-sm-6{order:6}.order-sm-7{order:7}.order-sm-8{order:8}.order-sm-9{order:9}.order-sm-10{order:10}.order-sm-11{order:11}.order-sm-12{order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.33333333%}.offset-sm-2{margin-left:16.66666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.33333333%}.offset-sm-5{margin-left:41.66666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.33333333%}.offset-sm-8{margin-left:66.66666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.33333333%}.offset-sm-11{margin-left:91.66666667%}}@media(min-width:768px){.col-md{flex-basis:0;flex-grow:1;max-width:100%}.col-md-auto{flex:0 0 auto;width:auto;max-width:none}.col-md-1{flex:0 0 8.33333333%;max-width:8.33333333%}.col-md-2{flex:0 0 16.66666667%;max-width:16.66666667%}.col-md-3{flex:0 0 25%;max-width:25%}.col-md-4{flex:0 0 33.33333333%;max-width:33.33333333%}.col-md-5{flex:0 0 41.66666667%;max-width:41.66666667%}.col-md-6{flex:0 0 50%;max-width:50%}.col-md-7{flex:0 0 58.33333333%;max-width:58.33333333%}.col-md-8{flex:0 0 66.66666667%;max-width:66.66666667%}.col-md-9{flex:0 0 75%;max-width:75%}.col-md-10{flex:0 0 83.33333333%;max-width:83.33333333%}.col-md-11{flex:0 0 91.66666667%;max-width:91.66666667%}.col-md-12{flex:0 0 100%;max-width:100%}.order-md-first{order:-1}.order-md-last{order:13}.order-md-0{order:0}.order-md-1{order:1}.order-md-2{order:2}.order-md-3{order:3}.order-md-4{order:4}.order-md-5{order:5}.order-md-6{order:6}.order-md-7{order:7}.order-md-8{order:8}.order-md-9{order:9}.order-md-10{order:10}.order-md-11{order:11}.order-md-12{order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.33333333%}.offset-md-2{margin-left:16.66666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.33333333%}.offset-md-5{margin-left:41.66666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.33333333%}.offset-md-8{margin-left:66.66666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.33333333%}.offset-md-11{margin-left:91.66666667%}}@media(min-width:992px){.col-lg{flex-basis:0;flex-grow:1;max-width:100%}.col-lg-auto{flex:0 0 auto;width:auto;max-width:none}.col-lg-1{flex:0 0 8.33333333%;max-width:8.33333333%}.col-lg-2{flex:0 0 16.66666667%;max-width:16.66666667%}.col-lg-3{flex:0 0 25%;max-width:25%}.col-lg-4{flex:0 0 33.33333333%;max-width:33.33333333%}.col-lg-5{flex:0 0 41.66666667%;max-width:41.66666667%}.col-lg-6{flex:0 0 50%;max-width:50%}.col-lg-7{flex:0 0 58.33333333%;max-width:58.33333333%}.col-lg-8{flex:0 0 66.66666667%;max-width:66.66666667%}.col-lg-9{flex:0 0 75%;max-width:75%}.col-lg-10{flex:0 0 83.33333333%;max-width:83.33333333%}.col-lg-11{flex:0 0 91.66666667%;max-width:91.66666667%}.col-lg-12{flex:0 0 100%;max-width:100%}.order-lg-first{order:-1}.order-lg-last{order:13}.order-lg-0{order:0}.order-lg-1{order:1}.order-lg-2{order:2}.order-lg-3{order:3}.order-lg-4{order:4}.order-lg-5{order:5}.order-lg-6{order:6}.order-lg-7{order:7}.order-lg-8{order:8}.order-lg-9{order:9}.order-lg-10{order:10}.order-lg-11{order:11}.order-lg-12{order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.33333333%}.offset-lg-2{margin-left:16.66666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.33333333%}.offset-lg-5{margin-left:41.66666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.33333333%}.offset-lg-8{margin-left:66.66666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.33333333%}.offset-lg-11{margin-left:91.66666667%}}@media(min-width:1280px){.col-xl{flex-basis:0;flex-grow:1;max-width:100%}.col-xl-auto{flex:0 0 auto;width:auto;max-width:none}.col-xl-1{flex:0 0 8.33333333%;max-width:8.33333333%}.col-xl-2{flex:0 0 16.66666667%;max-width:16.66666667%}.col-xl-3{flex:0 0 25%;max-width:25%}.col-xl-4{flex:0 0 33.33333333%;max-width:33.33333333%}.col-xl-5{flex:0 0 41.66666667%;max-width:41.66666667%}.col-xl-6{flex:0 0 50%;max-width:50%}.col-xl-7{flex:0 0 58.33333333%;max-width:58.33333333%}.col-xl-8{flex:0 0 66.66666667%;max-width:66.66666667%}.col-xl-9{flex:0 0 75%;max-width:75%}.col-xl-10{flex:0 0 83.33333333%;max-width:83.33333333%}.col-xl-11{flex:0 0 91.66666667%;max-width:91.66666667%}.col-xl-12{flex:0 0 100%;max-width:100%}.order-xl-first{order:-1}.order-xl-last{order:13}.order-xl-0{order:0}.order-xl-1{order:1}.order-xl-2{order:2}.order-xl-3{order:3}.order-xl-4{order:4}.order-xl-5{order:5}.order-xl-6{order:6}.order-xl-7{order:7}.order-xl-8{order:8}.order-xl-9{order:9}.order-xl-10{order:10}.order-xl-11{order:11}.order-xl-12{order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.33333333%}.offset-xl-2{margin-left:16.66666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.33333333%}.offset-xl-5{margin-left:41.66666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.33333333%}.offset-xl-8{margin-left:66.66666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.33333333%}.offset-xl-11{margin-left:91.66666667%}}.table,.text-wrap table{width:100%;max-width:100%;margin-bottom:1rem;background-color:initial}.table td,.table th,.text-wrap table td,.text-wrap table th{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th,.text-wrap table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody,.text-wrap table tbody+tbody{border-top:2px solid #dee2e6}.table .table,.table .text-wrap table,.text-wrap .table table,.text-wrap table .table,.text-wrap table table{background-color:#f5f7fb}.table-sm td,.table-sm th{padding:.3rem}.table-bordered,.table-bordered td,.table-bordered th,.text-wrap table,.text-wrap table td,.text-wrap table th{border:1px solid #dee2e6}.table-bordered thead td,.table-bordered thead th,.text-wrap table thead td,.text-wrap table thead th{border-bottom-width:2px}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.02)}.table-hover tbody tr:hover{background-color:rgba(0,0,0,.04)}.table-primary,.table-primary>td,.table-primary>th{background-color:#cbdbf2}.table-hover .table-primary:hover,.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#b7cded}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#dddfe2}.table-hover .table-secondary:hover,.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#cfd2d6}.table-success,.table-success>td,.table-success>th{background-color:#d2ecb8}.table-hover .table-success:hover,.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#c5e7a4}.table-info,.table-info>td,.table-info>th{background-color:#cbe7fb}.table-hover .table-info:hover,.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#b3dcf9}.table-warning,.table-warning>td,.table-warning>th{background-color:#fbeebc}.table-hover .table-warning:hover,.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#fae8a4}.table-danger,.table-danger>td,.table-danger>th{background-color:#f1c1c0}.table-hover .table-danger:hover,.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#ecacab}.table-light,.table-light>td,.table-light>th{background-color:#fdfdfe}.table-hover .table-light:hover,.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>td,.table-dark>th{background-color:#c6c8ca}.table-hover .table-dark:hover,.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>td,.table-active>th,.table-hover .table-active:hover,.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,.04)}.table .thead-dark th,.text-wrap table .thead-dark th{color:#f5f7fb;background-color:#212529;border-color:#32383e}.table .thead-light th,.text-wrap table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#f5f7fb;background-color:#212529}.table-dark td,.table-dark th,.table-dark thead th{border-color:#32383e}.table-dark.table-bordered,.text-wrap table.table-dark{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:hsla(0,0%,100%,.05)}.table-dark.table-hover tbody tr:hover{background-color:hsla(0,0%,100%,.075)}@media(max-width:575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-sm>.table-bordered,.text-wrap .table-responsive-sm>table{border:0}}@media(max-width:767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-md>.table-bordered,.text-wrap .table-responsive-md>table{border:0}}@media(max-width:991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-lg>.table-bordered,.text-wrap .table-responsive-lg>table{border:0}}@media(max-width:1279.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-xl>.table-bordered,.text-wrap .table-responsive-xl>table{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive>.table-bordered,.text-wrap .table-responsive>table{border:0}.form-control{display:block;width:100%;padding:.375rem .75rem;font-size:.9375rem;line-height:1.6;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,40,100,.12);border-radius:3px;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}.form-control::-ms-expand{background-color:initial;border:0}.form-control:focus{color:#495057;background-color:#fff;border-color:#1991eb;outline:0;box-shadow:0 0 0 2px rgba(70,127,207,.25)}.form-control::-webkit-input-placeholder{color:#adb5bd;opacity:1}.form-control:-ms-input-placeholder{color:#adb5bd;opacity:1}.form-control::-ms-input-placeholder{color:#adb5bd;opacity:1}.form-control::placeholder{color:#adb5bd;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#f8f9fa;opacity:1}select.form-control:not([size]):not([multiple]){height:2.375rem}select.form-control:focus::-ms-value{color:#495057;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.6}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.125rem;line-height:1.44444444}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.14285714}.form-control-plaintext{display:block;width:100%;padding-top:.375rem;padding-bottom:.375rem;margin-bottom:0;line-height:1.6;background-color:initial;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm,.input-group-lg>.form-control-plaintext.form-control,.input-group-lg>.input-group-append>.form-control-plaintext.btn,.input-group-lg>.input-group-append>.form-control-plaintext.input-group-text,.input-group-lg>.input-group-prepend>.form-control-plaintext.btn,.input-group-lg>.input-group-prepend>.form-control-plaintext.input-group-text,.input-group-sm>.form-control-plaintext.form-control,.input-group-sm>.input-group-append>.form-control-plaintext.btn,.input-group-sm>.input-group-append>.form-control-plaintext.input-group-text,.input-group-sm>.input-group-prepend>.form-control-plaintext.btn,.input-group-sm>.input-group-prepend>.form-control-plaintext.input-group-text{padding-right:0;padding-left:0}.form-control-sm,.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{padding:.25rem .5rem;font-size:.875rem;line-height:1.14285714;border-radius:3px}.input-group-sm>.input-group-append>select.btn:not([size]):not([multiple]),.input-group-sm>.input-group-append>select.input-group-text:not([size]):not([multiple]),.input-group-sm>.input-group-prepend>select.btn:not([size]):not([multiple]),.input-group-sm>.input-group-prepend>select.input-group-text:not([size]):not([multiple]),.input-group-sm>select.form-control:not([size]):not([multiple]),select.form-control-sm:not([size]):not([multiple]){height:calc(1.8125rem + 2px)}.form-control-lg,.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{padding:.5rem 1rem;font-size:1.125rem;line-height:1.44444444;border-radius:3px}.input-group-lg>.input-group-append>select.btn:not([size]):not([multiple]),.input-group-lg>.input-group-append>select.input-group-text:not([size]):not([multiple]),.input-group-lg>.input-group-prepend>select.btn:not([size]):not([multiple]),.input-group-lg>.input-group-prepend>select.input-group-text:not([size]):not([multiple]),.input-group-lg>select.form-control:not([size]):not([multiple]),select.form-control-lg:not([size]):not([multiple]){height:calc(2.6875rem + 2px)}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:flex;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label{color:#9aa0ac}.form-check-label{margin-bottom:0}.form-check-inline{display:inline-flex;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:87.5%;color:#5eba00}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.5rem;margin-top:.1rem;font-size:.875rem;line-height:1;color:#fff;background-color:rgba(94,186,0,.8);border-radius:.2rem}.custom-select.is-valid,.form-control.is-valid,.was-validated .custom-select:valid,.was-validated .form-control:valid{border-color:#5eba00}.custom-select.is-valid:focus,.form-control.is-valid:focus,.was-validated .custom-select:valid:focus,.was-validated .form-control:valid:focus{border-color:#5eba00;box-shadow:0 0 0 2px rgba(94,186,0,.25)}.custom-select.is-valid~.valid-feedback,.custom-select.is-valid~.valid-tooltip,.form-control.is-valid~.valid-feedback,.form-control.is-valid~.valid-tooltip,.was-validated .custom-select:valid~.valid-feedback,.was-validated .custom-select:valid~.valid-tooltip,.was-validated .form-control:valid~.valid-feedback,.was-validated .form-control:valid~.valid-tooltip{display:block}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#5eba00}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#5eba00}.custom-control-input.is-valid~.custom-control-label:before,.was-validated .custom-control-input:valid~.custom-control-label:before{background-color:#9eff3b}.custom-control-input.is-valid~.valid-feedback,.custom-control-input.is-valid~.valid-tooltip,.was-validated .custom-control-input:valid~.valid-feedback,.was-validated .custom-control-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid:checked~.custom-control-label:before,.was-validated .custom-control-input:valid:checked~.custom-control-label:before{background-color:#78ed00}.custom-control-input.is-valid:focus~.custom-control-label:before,.was-validated .custom-control-input:valid:focus~.custom-control-label:before{box-shadow:0 0 0 1px #f5f7fb,0 0 0 2px rgba(94,186,0,.25)}.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#5eba00}.custom-file-input.is-valid~.custom-file-label:before,.was-validated .custom-file-input:valid~.custom-file-label:before{border-color:inherit}.custom-file-input.is-valid~.valid-feedback,.custom-file-input.is-valid~.valid-tooltip,.was-validated .custom-file-input:valid~.valid-feedback,.was-validated .custom-file-input:valid~.valid-tooltip{display:block}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{box-shadow:0 0 0 2px rgba(94,186,0,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:87.5%;color:#cd201f}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.5rem;margin-top:.1rem;font-size:.875rem;line-height:1;color:#fff;background-color:rgba(205,32,31,.8);border-radius:.2rem}.custom-select.is-invalid,.form-control.is-invalid,.was-validated .custom-select:invalid,.was-validated .form-control:invalid{border-color:#cd201f}.custom-select.is-invalid:focus,.form-control.is-invalid:focus,.was-validated .custom-select:invalid:focus,.was-validated .form-control:invalid:focus{border-color:#cd201f;box-shadow:0 0 0 2px rgba(205,32,31,.25)}.custom-select.is-invalid~.invalid-feedback,.custom-select.is-invalid~.invalid-tooltip,.form-control.is-invalid~.invalid-feedback,.form-control.is-invalid~.invalid-tooltip,.was-validated .custom-select:invalid~.invalid-feedback,.was-validated .custom-select:invalid~.invalid-tooltip,.was-validated .form-control:invalid~.invalid-feedback,.was-validated .form-control:invalid~.invalid-tooltip{display:block}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#cd201f}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#cd201f}.custom-control-input.is-invalid~.custom-control-label:before,.was-validated .custom-control-input:invalid~.custom-control-label:before{background-color:#ec8080}.custom-control-input.is-invalid~.invalid-feedback,.custom-control-input.is-invalid~.invalid-tooltip,.was-validated .custom-control-input:invalid~.invalid-feedback,.was-validated .custom-control-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid:checked~.custom-control-label:before,.was-validated .custom-control-input:invalid:checked~.custom-control-label:before{background-color:#e23e3d}.custom-control-input.is-invalid:focus~.custom-control-label:before,.was-validated .custom-control-input:invalid:focus~.custom-control-label:before{box-shadow:0 0 0 1px #f5f7fb,0 0 0 2px rgba(205,32,31,.25)}.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#cd201f}.custom-file-input.is-invalid~.custom-file-label:before,.was-validated .custom-file-input:invalid~.custom-file-label:before{border-color:inherit}.custom-file-input.is-invalid~.invalid-feedback,.custom-file-input.is-invalid~.invalid-tooltip,.was-validated .custom-file-input:invalid~.invalid-feedback,.was-validated .custom-file-input:invalid~.invalid-tooltip{display:block}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{box-shadow:0 0 0 2px rgba(205,32,31,.25)}.form-inline{display:flex;flex-flow:row wrap;align-items:center}.form-inline .form-check{width:100%}@media(min-width:576px){.form-inline label{justify-content:center}.form-inline .form-group,.form-inline label{display:flex;align-items:center;margin-bottom:0}.form-inline .form-group{flex:0 0 auto;flex-flow:row wrap}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .input-group{width:auto}.form-inline .form-check{display:flex;align-items:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{align-items:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;text-align:center;white-space:nowrap;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border:1px solid transparent;padding:.375rem .75rem;font-size:.9375rem;line-height:1.84615385;border-radius:3px;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}.btn:focus,.btn:hover{text-decoration:none}.btn.focus,.btn:focus{outline:0;box-shadow:0 0 0 2px rgba(70,127,207,.25)}.btn.disabled,.btn:disabled{opacity:.65}.btn:not(:disabled):not(.disabled){cursor:pointer}.btn:not(:disabled):not(.disabled).active,.btn:not(:disabled):not(.disabled):active{background-image:none}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#467fcf;border-color:#467fcf}.btn-primary:hover{color:#fff;background-color:#316cbe;border-color:#2f66b3}.btn-primary.focus,.btn-primary:focus{box-shadow:0 0 0 2px rgba(70,127,207,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#467fcf;border-color:#467fcf}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#2f66b3;border-color:#2c60a9}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(70,127,207,.5)}.btn-secondary{color:#fff;background-color:#868e96;border-color:#868e96}.btn-secondary:hover{color:#fff;background-color:#727b84;border-color:#6c757d}.btn-secondary.focus,.btn-secondary:focus{box-shadow:0 0 0 2px rgba(134,142,150,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#868e96;border-color:#868e96}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#666e76}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(134,142,150,.5)}.btn-success{color:#fff;background-color:#5eba00;border-color:#5eba00}.btn-success:hover{color:#fff;background-color:#4b9400;border-color:#448700}.btn-success.focus,.btn-success:focus{box-shadow:0 0 0 2px rgba(94,186,0,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#5eba00;border-color:#5eba00}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#448700;border-color:#3e7a00}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(94,186,0,.5)}.btn-info{color:#fff;background-color:#45aaf2;border-color:#45aaf2}.btn-info:hover{color:#fff;background-color:#219af0;border-color:#1594ef}.btn-info.focus,.btn-info:focus{box-shadow:0 0 0 2px rgba(69,170,242,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#45aaf2;border-color:#45aaf2}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#1594ef;border-color:#108ee7}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(69,170,242,.5)}.btn-warning{color:#fff;background-color:#f1c40f;border-color:#f1c40f}.btn-warning:hover{color:#fff;background-color:#cea70c;border-color:#c29d0b}.btn-warning.focus,.btn-warning:focus{box-shadow:0 0 0 2px rgba(241,196,15,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#fff;background-color:#f1c40f;border-color:#f1c40f}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#fff;background-color:#c29d0b;border-color:#b6940b}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(241,196,15,.5)}.btn-danger{color:#fff;background-color:#cd201f;border-color:#cd201f}.btn-danger:hover{color:#fff;background-color:#ac1b1a;border-color:#a11918}.btn-danger.focus,.btn-danger:focus{box-shadow:0 0 0 2px rgba(205,32,31,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#cd201f;border-color:#cd201f}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#a11918;border-color:#961717}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(205,32,31,.5)}.btn-light{color:#495057;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#495057;background-color:#e2e6ea;border-color:#dae0e5}.btn-light.focus,.btn-light:focus{box-shadow:0 0 0 2px rgba(248,249,250,.5)}.btn-light.disabled,.btn-light:disabled{color:#495057;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#495057;background-color:#dae0e5;border-color:#d3d9df}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(248,249,250,.5)}.btn-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark.focus,.btn-dark:focus{box-shadow:0 0 0 2px rgba(52,58,64,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(52,58,64,.5)}.btn-outline-primary{color:#467fcf;background-color:initial;background-image:none;border-color:#467fcf}.btn-outline-primary:hover{color:#fff;background-color:#467fcf;border-color:#467fcf}.btn-outline-primary.focus,.btn-outline-primary:focus{box-shadow:0 0 0 2px rgba(70,127,207,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#467fcf;background-color:initial}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#467fcf;border-color:#467fcf}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(70,127,207,.5)}.btn-outline-secondary{color:#868e96;background-color:initial;background-image:none;border-color:#868e96}.btn-outline-secondary:hover{color:#fff;background-color:#868e96;border-color:#868e96}.btn-outline-secondary.focus,.btn-outline-secondary:focus{box-shadow:0 0 0 2px rgba(134,142,150,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#868e96;background-color:initial}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#868e96;border-color:#868e96}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(134,142,150,.5)}.btn-outline-success{color:#5eba00;background-color:initial;background-image:none;border-color:#5eba00}.btn-outline-success:hover{color:#fff;background-color:#5eba00;border-color:#5eba00}.btn-outline-success.focus,.btn-outline-success:focus{box-shadow:0 0 0 2px rgba(94,186,0,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#5eba00;background-color:initial}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#5eba00;border-color:#5eba00}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(94,186,0,.5)}.btn-outline-info{color:#45aaf2;background-color:initial;background-image:none;border-color:#45aaf2}.btn-outline-info:hover{color:#fff;background-color:#45aaf2;border-color:#45aaf2}.btn-outline-info.focus,.btn-outline-info:focus{box-shadow:0 0 0 2px rgba(69,170,242,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#45aaf2;background-color:initial}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#45aaf2;border-color:#45aaf2}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(69,170,242,.5)}.btn-outline-warning{color:#f1c40f;background-color:initial;background-image:none;border-color:#f1c40f}.btn-outline-warning:hover{color:#fff;background-color:#f1c40f;border-color:#f1c40f}.btn-outline-warning.focus,.btn-outline-warning:focus{box-shadow:0 0 0 2px rgba(241,196,15,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#f1c40f;background-color:initial}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#fff;background-color:#f1c40f;border-color:#f1c40f}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(241,196,15,.5)}.btn-outline-danger{color:#cd201f;background-color:initial;background-image:none;border-color:#cd201f}.btn-outline-danger:hover{color:#fff;background-color:#cd201f;border-color:#cd201f}.btn-outline-danger.focus,.btn-outline-danger:focus{box-shadow:0 0 0 2px rgba(205,32,31,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#cd201f;background-color:initial}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#cd201f;border-color:#cd201f}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(205,32,31,.5)}.btn-outline-light{color:#f8f9fa;background-color:initial;background-image:none;border-color:#f8f9fa}.btn-outline-light:hover{color:#495057;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.focus,.btn-outline-light:focus{box-shadow:0 0 0 2px rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:initial}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#495057;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(248,249,250,.5)}.btn-outline-dark{color:#343a40;background-color:initial;background-image:none;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.focus,.btn-outline-dark:focus{box-shadow:0 0 0 2px rgba(52,58,64,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:initial}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(52,58,64,.5)}.btn-link{font-weight:400;color:#467fcf;background-color:initial}.btn-link:hover{color:#295a9f;background-color:initial}.btn-link.focus,.btn-link:focus,.btn-link:hover{text-decoration:underline;border-color:transparent}.btn-link.focus,.btn-link:focus{box-shadow:none}.btn-link.disabled,.btn-link:disabled{color:#868e96}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.125rem;line-height:1.625;border-radius:3px}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;line-height:1.33333333;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;transition:opacity .15s linear}.fade.show{opacity:1}.collapse{display:none}.collapse.show{display:block}tr.collapse.show{display:table-row}tbody.collapse.show{display:table-row-group}.collapsing{height:0;overflow:hidden;transition:height .35s ease}.collapsing,.dropdown,.dropup{position:relative}.dropdown-toggle:after{display:inline-block;width:0;height:0;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:.9375rem;color:#495057;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,40,100,.12);border-radius:3px}.dropup .dropdown-menu{margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle:after{display:inline-block;width:0;height:0;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty:after{margin-left:0}.dropright .dropdown-menu{margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle:after{display:inline-block;width:0;height:0;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty:after{margin-left:0}.dropright .dropdown-toggle:after{vertical-align:0}.dropleft .dropdown-menu{margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle:after{display:inline-block;width:0;height:0;margin-left:.255em;vertical-align:.255em;content:"";display:none}.dropleft .dropdown-toggle:before{display:inline-block;width:0;height:0;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty:after{margin-left:0}.dropleft .dropdown-toggle:before{vertical-align:0}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:initial;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#16181b;text-decoration:none;background-color:#f8f9fa}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#467fcf}.dropdown-item.disabled,.dropdown-item:disabled{color:#868e96;background-color:initial}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#868e96;white-space:nowrap}.btn-group,.btn-group-vertical{position:relative;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;flex:0 1 auto}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:1}.btn-group-vertical .btn+.btn,.btn-group-vertical .btn+.btn-group,.btn-group-vertical .btn-group+.btn,.btn-group-vertical .btn-group+.btn-group,.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{display:flex;flex-wrap:wrap;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split:after{margin-left:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{flex-direction:column;align-items:flex-start;justify-content:center}.btn-group-vertical .btn,.btn-group-vertical .btn-group{width:100%}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio],.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:flex;flex-wrap:wrap;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control{position:relative;flex:1 1 auto;width:1%;margin-bottom:0}.input-group>.custom-file:focus,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control{margin-left:-1px}.input-group>.custom-select:not(:last-child),.input-group>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:flex;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label:before{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label,.input-group>.custom-file:not(:first-child) .custom-file-label:before{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-append,.input-group-prepend{display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:flex;align-items:center;padding:.375rem .75rem;margin-bottom:0;font-size:.9375rem;font-weight:400;line-height:1.6;color:#495057;text-align:center;white-space:nowrap;background-color:#fbfbfc;border:1px solid rgba(0,40,100,.12);border-radius:3px}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;display:block;min-height:1.5rem;padding-left:1.5rem}.custom-control-inline{display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;z-index:-1;opacity:0}.custom-control-input:checked~.custom-control-label:before{color:#fff;background-color:#467fcf}.custom-control-input:focus~.custom-control-label:before{box-shadow:0 0 0 1px #f5f7fb,0 0 0 2px rgba(70,127,207,.25)}.custom-control-input:active~.custom-control-label:before{color:#fff;background-color:#d4e1f4}.custom-control-input:disabled~.custom-control-label{color:#868e96}.custom-control-input:disabled~.custom-control-label:before{background-color:#e9ecef}.custom-control-label{margin-bottom:0}.custom-control-label:before{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:#dee2e6}.custom-control-label:after,.custom-control-label:before{position:absolute;top:.25rem;left:0;display:block;width:1rem;height:1rem;content:""}.custom-control-label:after{background-repeat:no-repeat;background-position:50%;background-size:50% 50%}.custom-checkbox .custom-control-label:before{border-radius:3px}.custom-checkbox .custom-control-input:checked~.custom-control-label:before{background-color:#467fcf}.custom-checkbox .custom-control-input:checked~.custom-control-label:after{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label:before{background-color:#467fcf}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label:after{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3E%3Cpath stroke='%23fff' d='M0 2h4'/%3E%3C/svg%3E")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label:before{background-color:rgba(70,127,207,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label:before{background-color:rgba(70,127,207,.5)}.custom-radio .custom-control-label:before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label:before{background-color:#467fcf}.custom-radio .custom-control-input:checked~.custom-control-label:after{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23fff'/%3E%3C/svg%3E")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label:before{background-color:rgba(70,127,207,.5)}.custom-select{display:inline-block;width:100%;height:2.375rem;padding:.5rem 1.75rem .5rem .75rem;line-height:1.5;color:#495057;vertical-align:middle;background:#fff url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 5'%3E%3Cpath fill='#999' d='M0 0L10 0L5 5L0 0'/%3E%3C/svg%3E") no-repeat right .75rem center;background-size:8px 10px;border:1px solid rgba(0,40,100,.12);border-radius:3px;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#1991eb;outline:0;box-shadow:inset 0 1px 2px rgba(0,0,0,.075),0 0 5px rgba(25,145,235,.5)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#868e96;background-color:#e9ecef}.custom-select::-ms-expand{opacity:0}.custom-select-sm{height:calc(1.8125rem + 2px);font-size:75%}.custom-select-lg,.custom-select-sm{padding-top:.5rem;padding-bottom:.5rem}.custom-select-lg{height:calc(2.6875rem + 2px);font-size:125%}.custom-file{display:inline-block;margin-bottom:0}.custom-file,.custom-file-input{position:relative;width:100%;height:2.375rem}.custom-file-input{z-index:2;margin:0;opacity:0}.custom-file-input:focus~.custom-file-control{border-color:#1991eb;box-shadow:0 0 0 2px rgba(70,127,207,.25)}.custom-file-input:focus~.custom-file-control:before{border-color:#1991eb}.custom-file-input:lang(en)~.custom-file-label:after{content:"Browse"}.custom-file-label{left:0;z-index:1;height:2.375rem;background-color:#fff;border:1px solid rgba(0,40,100,.12);border-radius:3px}.custom-file-label,.custom-file-label:after{position:absolute;top:0;right:0;padding:.375rem .75rem;line-height:1.5;color:#495057}.custom-file-label:after{bottom:0;z-index:3;display:block;height:calc(2.375rem - 2px);content:"Browse";background-color:#fbfbfc;border-left:1px solid rgba(0,40,100,.12);border-radius:0 3px 3px 0}.nav{display:flex;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#868e96}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#868e96;background-color:initial;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#f5f7fb;border-color:#dee2e6 #dee2e6 #f5f7fb}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:3px}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#467fcf}.nav-fill .nav-item{flex:1 1 auto;text-align:center}.nav-justified .nav-item{flex-basis:0;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;padding:.5rem 1rem}.navbar,.navbar>.container,.navbar>.container-fluid{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.359375rem;padding-bottom:.359375rem;margin-right:1rem;font-size:1.125rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:flex;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{flex-basis:100%;flex-grow:1;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.125rem;line-height:1;background-color:initial;border:1px solid transparent;border-radius:3px}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler:not(:disabled):not(.disabled){cursor:pointer}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat 50%;background-size:100% 100%}@media(max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{padding-right:0;padding-left:0}}@media(min-width:576px){.navbar-expand-sm{flex-flow:row nowrap;justify-content:flex-start}.navbar-expand-sm .navbar-nav{flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .dropdown-menu-right{right:0;left:auto}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:flex !important;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}.navbar-expand-sm .dropup .dropdown-menu{top:auto;bottom:100%}}@media(max-width:767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{padding-right:0;padding-left:0}}@media(min-width:768px){.navbar-expand-md{flex-flow:row nowrap;justify-content:flex-start}.navbar-expand-md .navbar-nav{flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .dropdown-menu-right{right:0;left:auto}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:flex !important;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}.navbar-expand-md .dropup .dropdown-menu{top:auto;bottom:100%}}@media(max-width:991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{padding-right:0;padding-left:0}}@media(min-width:992px){.navbar-expand-lg{flex-flow:row nowrap;justify-content:flex-start}.navbar-expand-lg .navbar-nav{flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .dropdown-menu-right{right:0;left:auto}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:flex !important;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}.navbar-expand-lg .dropup .dropdown-menu{top:auto;bottom:100%}}@media(max-width:1279.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{padding-right:0;padding-left:0}}@media(min-width:1280px){.navbar-expand-xl{flex-flow:row nowrap;justify-content:flex-start}.navbar-expand-xl .navbar-nav{flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .dropdown-menu-right{right:0;left:auto}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:flex !important;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}.navbar-expand-xl .dropup .dropdown-menu{top:auto;bottom:100%}}.navbar-expand{flex-flow:row nowrap;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .dropdown-menu-right{right:0;left:auto}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid{flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:flex !important;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-expand .dropup .dropdown-menu{top:auto;bottom:100%}.navbar-light .navbar-brand,.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a,.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand,.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:hsla(0,0%,100%,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:hsla(0,0%,100%,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:hsla(0,0%,100%,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:hsla(0,0%,100%,.5);border-color:hsla(0,0%,100%,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E")}.navbar-dark .navbar-text{color:hsla(0,0%,100%,.5)}.navbar-dark .navbar-text a,.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{display:flex;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:initial;border:1px solid rgba(0,40,100,.12);border-radius:3px}.card>hr{margin-right:0;margin-left:0}.card>.list-group:first-child .list-group-item:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.card>.list-group:last-child .list-group-item:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.card-subtitle{margin-top:-.75rem}.card-subtitle,.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.5rem}.card-header{padding:1.5rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,40,100,.12)}.card-header:first-child{border-radius:2px 2px 0 0}.card-header+.list-group .list-group-item:first-child{border-top:0}.card-footer{padding:1.5rem;background-color:rgba(0,0,0,.03)}.card-footer:last-child{border-radius:0 0 2px 2px}.card-header-tabs{margin-bottom:-1.5rem}.card-header-pills,.card-header-tabs{margin-right:-.75rem;margin-left:-.75rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img{width:100%;border-radius:2px}.card-img-top{width:100%;border-top-left-radius:2px;border-top-right-radius:2px}.card-img-bottom{width:100%;border-bottom-right-radius:2px;border-bottom-left-radius:2px}.card-deck{display:flex;flex-direction:column}.card-deck .card{margin-bottom:.75rem}@media(min-width:576px){.card-deck{flex-flow:row wrap;margin-right:-.75rem;margin-left:-.75rem}.card-deck .card{display:flex;flex:1 0;flex-direction:column;margin-right:.75rem;margin-bottom:0;margin-left:.75rem}}.card-group{display:flex;flex-direction:column}.card-group>.card{margin-bottom:.75rem}@media(min-width:576px){.card-group{flex-flow:row wrap}.card-group>.card{flex:1 0;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:first-child{border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:first-child .card-header,.card-group>.card:first-child .card-img-top{border-top-right-radius:0}.card-group>.card:first-child .card-footer,.card-group>.card:first-child .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:last-child{border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:last-child .card-header,.card-group>.card:last-child .card-img-top{border-top-left-radius:0}.card-group>.card:last-child .card-footer,.card-group>.card:last-child .card-img-bottom{border-bottom-left-radius:0}.card-group>.card:only-child{border-radius:3px}.card-group>.card:only-child .card-header,.card-group>.card:only-child .card-img-top{border-top-left-radius:3px;border-top-right-radius:3px}.card-group>.card:only-child .card-footer,.card-group>.card:only-child .card-img-bottom{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.card-group>.card:not(:first-child):not(:last-child):not(:only-child),.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-footer,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-header,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-img-bottom,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-img-top{border-radius:0}}.card-columns .card{margin-bottom:1.5rem}@media(min-width:576px){.card-columns{-webkit-column-count:3;column-count:3;-webkit-column-gap:1.25rem;-moz-column-gap:1.25rem;grid-column-gap:1.25rem;column-gap:1.25rem}.card-columns .card{display:inline-block;width:100%}}.breadcrumb{display:flex;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:3px}.breadcrumb-item+.breadcrumb-item:before{display:inline-block;padding-right:.5rem;padding-left:.5rem;color:#868e96;content:"/"}.breadcrumb-item+.breadcrumb-item:hover:before{text-decoration:underline;text-decoration:none}.breadcrumb-item.active{color:#868e96}.pagination{display:flex;padding-left:0;list-style:none;border-radius:3px}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#495057;background-color:#fff;border:1px solid #dee2e6}.page-link:hover{color:#295a9f;text-decoration:none;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:2;outline:0;box-shadow:0 0 0 2px rgba(70,127,207,.25)}.page-link:not(:disabled):not(.disabled){cursor:pointer}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:3px;border-bottom-left-radius:3px}.page-item:last-child .page-link{border-top-right-radius:3px;border-bottom-right-radius:3px}.page-item.active .page-link{z-index:1;color:#fff;background-color:#467fcf;border-color:#467fcf}.page-item.disabled .page-link{color:#ced4da;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dee2e6}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.125rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:3px;border-bottom-right-radius:3px}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:3px;border-bottom-right-radius:3px}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:600;line-height:1;text-align:center;white-space:nowrap;vertical-align:initial;border-radius:3px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#467fcf}.badge-primary[href]:focus,.badge-primary[href]:hover{color:#fff;text-decoration:none;background-color:#2f66b3}.badge-secondary{color:#fff;background-color:#868e96}.badge-secondary[href]:focus,.badge-secondary[href]:hover{color:#fff;text-decoration:none;background-color:#6c757d}.badge-success{color:#fff;background-color:#5eba00}.badge-success[href]:focus,.badge-success[href]:hover{color:#fff;text-decoration:none;background-color:#448700}.badge-info{color:#fff;background-color:#45aaf2}.badge-info[href]:focus,.badge-info[href]:hover{color:#fff;text-decoration:none;background-color:#1594ef}.badge-warning{color:#fff;background-color:#f1c40f}.badge-warning[href]:focus,.badge-warning[href]:hover{color:#fff;text-decoration:none;background-color:#c29d0b}.badge-danger{color:#fff;background-color:#cd201f}.badge-danger[href]:focus,.badge-danger[href]:hover{color:#fff;text-decoration:none;background-color:#a11918}.badge-light{color:#495057;background-color:#f8f9fa}.badge-light[href]:focus,.badge-light[href]:hover{color:#495057;text-decoration:none;background-color:#dae0e5}.badge-dark{color:#fff;background-color:#343a40}.badge-dark[href]:focus,.badge-dark[href]:hover{color:#fff;text-decoration:none;background-color:#1d2124}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:3px}@media(min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:3px}.alert-heading{color:inherit}.alert-link{font-weight:600}.alert-dismissible{padding-right:3.90625rem}.alert-dismissible .close{position:absolute;top:0;right:0;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#24426c;background-color:#dae5f5;border-color:#cbdbf2}.alert-primary hr{border-top-color:#b7cded}.alert-primary .alert-link{color:#172b46}.alert-secondary{color:#464a4e;background-color:#e7e8ea;border-color:#dddfe2}.alert-secondary hr{border-top-color:#cfd2d6}.alert-secondary .alert-link{color:#2e3133}.alert-success{color:#316100;background-color:#dff1cc;border-color:#d2ecb8}.alert-success hr{border-top-color:#c5e7a4}.alert-success .alert-link{color:#172e00}.alert-info{color:#24587e;background-color:#daeefc;border-color:#cbe7fb}.alert-info hr{border-top-color:#b3dcf9}.alert-info .alert-link{color:#193c56}.alert-warning{color:#7d6608;background-color:#fcf3cf;border-color:#fbeebc}.alert-warning hr{border-top-color:#fae8a4}.alert-warning .alert-link{color:#4d3f05}.alert-danger{color:#6b1110;background-color:#f5d2d2;border-color:#f1c1c0}.alert-danger hr{border-top-color:#ecacab}.alert-danger .alert-link{color:#3f0a09}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}@-webkit-keyframes progress-bar-stripes{0%{background-position:1rem 0}to{background-position:0 0}}@keyframes progress-bar-stripes{0%{background-position:1rem 0}to{background-position:0 0}}.progress{display:flex;height:1rem;overflow:hidden;font-size:.703125rem;background-color:#e9ecef;border-radius:3px}.progress-bar{display:flex;flex-direction:column;justify-content:center;color:#fff;text-align:center;background-color:#467fcf;transition:width .6s ease}.progress-bar-striped{background-image:linear-gradient(45deg,hsla(0,0%,100%,.15) 25%,transparent 0,transparent 50%,hsla(0,0%,100%,.15) 0,hsla(0,0%,100%,.15) 75%,transparent 0,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:progress-bar-stripes 1s linear infinite;animation:progress-bar-stripes 1s linear infinite}.media{display:flex;align-items:flex-start}.media-body{flex:1 1}.list-group{display:flex;flex-direction:column;padding-left:0;margin-bottom:0}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#495057;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;margin-bottom:-1px;background-color:#fff;border:1px solid rgba(0,40,100,.12)}.list-group-item:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.list-group-item:focus,.list-group-item:hover{z-index:1;text-decoration:none}.list-group-item.disabled,.list-group-item:disabled{color:#868e96;background-color:#fff}.list-group-item.active{z-index:2;color:#467fcf;background-color:#f8fafd;border-color:rgba(0,40,100,.12)}.list-group-flush .list-group-item{border-right:0;border-left:0;border-radius:0}.list-group-flush:first-child .list-group-item:first-child{border-top:0}.list-group-flush:last-child .list-group-item:last-child{border-bottom:0}.list-group-item-primary{color:#24426c;background-color:#cbdbf2}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#24426c;background-color:#b7cded}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#24426c;border-color:#24426c}.list-group-item-secondary{color:#464a4e;background-color:#dddfe2}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#464a4e;background-color:#cfd2d6}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#464a4e;border-color:#464a4e}.list-group-item-success{color:#316100;background-color:#d2ecb8}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#316100;background-color:#c5e7a4}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#316100;border-color:#316100}.list-group-item-info{color:#24587e;background-color:#cbe7fb}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#24587e;background-color:#b3dcf9}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#24587e;border-color:#24587e}.list-group-item-warning{color:#7d6608;background-color:#fbeebc}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#7d6608;background-color:#fae8a4}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#7d6608;border-color:#7d6608}.list-group-item-danger{color:#6b1110;background-color:#f1c1c0}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#6b1110;background-color:#ecacab}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#6b1110;border-color:#6b1110}.list-group-item-light{color:#818182;background-color:#fdfdfe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#818182;background-color:#ececf6}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#818182;border-color:#818182}.list-group-item-dark{color:#1b1e21;background-color:#c6c8ca}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#1b1e21;background-color:#b9bbbe}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1b1e21;border-color:#1b1e21}.close{float:right;font-size:1.40625rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:focus,.close:hover{color:#000;text-decoration:none;opacity:.75}.close:not(:disabled):not(.disabled){cursor:pointer}button.close{padding:0;background-color:initial;border:0;-webkit-appearance:none}.modal,.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;outline:0}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out;-webkit-transform:translateY(-25%);transform:translateY(-25%)}.modal.show .modal-dialog{-webkit-transform:translate(0);transform:translate(0)}.modal-dialog-centered{display:flex;align-items:center;min-height:calc(100% - 1rem)}.modal-content{position:relative;display:flex;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:3px;outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:flex;align-items:flex-start;justify-content:space-between;padding:1rem;border-bottom:1px solid #e9ecef;border-top-left-radius:3px;border-top-right-radius:3px}.modal-header .close{padding:1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;flex:1 1 auto;padding:1rem}.modal-footer{display:flex;align-items:center;justify-content:flex-end;padding:1rem;border-top:1px solid #e9ecef}.modal-footer>:not(:first-child){margin-left:.25rem}.modal-footer>:not(:last-child){margin-right:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media(min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-sm{max-width:300px}}@media(min-width:992px){.modal-lg{max-width:800px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:Source Sans Pro,-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica Neue,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow:before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow:before,.bs-tooltip-top .arrow:before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow:before,.bs-tooltip-right .arrow:before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow:before,.bs-tooltip-bottom .arrow:before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow:before,.bs-tooltip-left .arrow:before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:3px}.popover{top:0;left:0;z-index:1060;max-width:276px;font-family:Source Sans Pro,-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica Neue,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid #dee3eb;border-radius:3px}.popover,.popover .arrow{position:absolute;display:block}.popover .arrow{width:.5rem;height:.5rem;margin:0 3px}.popover .arrow:after,.popover .arrow:before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top] .arrow,.bs-popover-top .arrow{bottom:calc(-.5rem + -1px)}.bs-popover-auto[x-placement^=top] .arrow:after,.bs-popover-auto[x-placement^=top] .arrow:before,.bs-popover-top .arrow:after,.bs-popover-top .arrow:before{border-width:.5rem .25rem 0}.bs-popover-auto[x-placement^=top] .arrow:before,.bs-popover-top .arrow:before{bottom:0;border-top-color:#dee3eb}.bs-popover-auto[x-placement^=top] .arrow:after,.bs-popover-top .arrow:after{bottom:1px;border-top-color:#fff}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right] .arrow,.bs-popover-right .arrow{left:calc(-.5rem + -1px);width:.5rem;height:.5rem;margin:3px 0}.bs-popover-auto[x-placement^=right] .arrow:after,.bs-popover-auto[x-placement^=right] .arrow:before,.bs-popover-right .arrow:after,.bs-popover-right .arrow:before{border-width:.25rem .5rem .25rem 0}.bs-popover-auto[x-placement^=right] .arrow:before,.bs-popover-right .arrow:before{left:0;border-right-color:#dee3eb}.bs-popover-auto[x-placement^=right] .arrow:after,.bs-popover-right .arrow:after{left:1px;border-right-color:#fff}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom] .arrow,.bs-popover-bottom .arrow{top:calc(-.5rem + -1px)}.bs-popover-auto[x-placement^=bottom] .arrow:after,.bs-popover-auto[x-placement^=bottom] .arrow:before,.bs-popover-bottom .arrow:after,.bs-popover-bottom .arrow:before{border-width:0 .25rem .5rem}.bs-popover-auto[x-placement^=bottom] .arrow:before,.bs-popover-bottom .arrow:before{top:0;border-bottom-color:#dee3eb}.bs-popover-auto[x-placement^=bottom] .arrow:after,.bs-popover-bottom .arrow:after{top:1px;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header:before,.bs-popover-bottom .popover-header:before{position:absolute;top:0;left:50%;display:block;width:.5rem;margin-left:-.25rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left] .arrow,.bs-popover-left .arrow{right:calc(-.5rem + -1px);width:.5rem;height:.5rem;margin:3px 0}.bs-popover-auto[x-placement^=left] .arrow:after,.bs-popover-auto[x-placement^=left] .arrow:before,.bs-popover-left .arrow:after,.bs-popover-left .arrow:before{border-width:.25rem 0 .25rem .5rem}.bs-popover-auto[x-placement^=left] .arrow:before,.bs-popover-left .arrow:before{right:0;border-left-color:#dee3eb}.bs-popover-auto[x-placement^=left] .arrow:after,.bs-popover-left .arrow:after{right:1px;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:.9375rem;color:inherit;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:2px;border-top-right-radius:2px}.popover-header:empty{display:none}.popover-body{padding:.75rem 1rem;color:#6e7687}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-item{position:relative;display:none;align-items:center;width:100%;transition:-webkit-transform .6s ease;transition:transform .6s ease;transition:transform .6s ease,-webkit-transform .6s ease;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.carousel-item-next,.carousel-item-prev{position:absolute;top:0}.carousel-item-next.carousel-item-left,.carousel-item-prev.carousel-item-right{-webkit-transform:translateX(0);transform:translateX(0)}@supports(((-webkit-transform-style:preserve-3d) or(transform-style:preserve-3d))){.carousel-item-next.carousel-item-left,.carousel-item-prev.carousel-item-right{-webkit-transform:translateZ(0);transform:translateZ(0)}}.active.carousel-item-right,.carousel-item-next{-webkit-transform:translateX(100%);transform:translateX(100%)}@supports(((-webkit-transform-style:preserve-3d) or(transform-style:preserve-3d))){.active.carousel-item-right,.carousel-item-next{-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}}.active.carousel-item-left,.carousel-item-prev{-webkit-transform:translateX(-100%);transform:translateX(-100%)}@supports(((-webkit-transform-style:preserve-3d) or(transform-style:preserve-3d))){.active.carousel-item-left,.carousel-item-prev{-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;display:flex;align-items:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:.5}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:transparent no-repeat 50%;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3E%3C/svg%3E")}.carousel-control-next-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3E%3C/svg%3E")}.carousel-indicators{position:absolute;right:0;bottom:10px;left:0;z-index:15;display:flex;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{position:relative;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;background-color:hsla(0,0%,100%,.5)}.carousel-indicators li:before{top:-10px}.carousel-indicators li:after,.carousel-indicators li:before{position:absolute;left:0;display:inline-block;width:100%;height:10px;content:""}.carousel-indicators li:after{bottom:-10px}.carousel-indicators .active{background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}.align-baseline{vertical-align:initial !important}.align-top{vertical-align:top !important}.align-middle{vertical-align:middle !important}.align-bottom{vertical-align:bottom !important}.align-text-bottom{vertical-align:text-bottom !important}.align-text-top{vertical-align:text-top !important}.bg-primary{background-color:#467fcf !important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#2f66b3 !important}.bg-secondary{background-color:#868e96 !important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#6c757d !important}.bg-success{background-color:#5eba00 !important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#448700 !important}.bg-info{background-color:#45aaf2 !important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#1594ef !important}.bg-warning{background-color:#f1c40f !important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#c29d0b !important}.bg-danger{background-color:#cd201f !important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#a11918 !important}.bg-light{background-color:#f8f9fa !important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#dae0e5 !important}.bg-dark{background-color:#343a40 !important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#1d2124 !important}.bg-transparent{background-color:initial !important}.border{border:1px solid rgba(0,40,100,.12) !important}.border-top{border-top:1px solid rgba(0,40,100,.12) !important}.border-right{border-right:1px solid rgba(0,40,100,.12) !important}.border-bottom{border-bottom:1px solid rgba(0,40,100,.12) !important}.border-left{border-left:1px solid rgba(0,40,100,.12) !important}.border-0{border:0 !important}.border-top-0{border-top:0 !important}.border-right-0{border-right:0 !important}.border-bottom-0{border-bottom:0 !important}.border-left-0{border-left:0 !important}.border-primary{border-color:#467fcf !important}.border-secondary{border-color:#868e96 !important}.border-success{border-color:#5eba00 !important}.border-info{border-color:#45aaf2 !important}.border-warning{border-color:#f1c40f !important}.border-danger{border-color:#cd201f !important}.border-light{border-color:#f8f9fa !important}.border-dark{border-color:#343a40 !important}.border-white{border-color:#fff !important}.rounded{border-radius:3px !important}.rounded-top{border-top-left-radius:3px !important}.rounded-right,.rounded-top{border-top-right-radius:3px !important}.rounded-bottom,.rounded-right{border-bottom-right-radius:3px !important}.rounded-bottom,.rounded-left{border-bottom-left-radius:3px !important}.rounded-left{border-top-left-radius:3px !important}.rounded-circle{border-radius:50% !important}.rounded-0{border-radius:0 !important}.clearfix:after{display:block;clear:both;content:""}.d-none{display:none !important}.d-inline{display:inline !important}.d-inline-block{display:inline-block !important}.d-block{display:block !important}.d-table{display:table !important}.d-table-row{display:table-row !important}.d-table-cell{display:table-cell !important}.d-flex{display:flex !important}.d-inline-flex{display:inline-flex !important}@media(min-width:576px){.d-sm-none{display:none !important}.d-sm-inline{display:inline !important}.d-sm-inline-block{display:inline-block !important}.d-sm-block{display:block !important}.d-sm-table{display:table !important}.d-sm-table-row{display:table-row !important}.d-sm-table-cell{display:table-cell !important}.d-sm-flex{display:flex !important}.d-sm-inline-flex{display:inline-flex !important}}@media(min-width:768px){.d-md-none{display:none !important}.d-md-inline{display:inline !important}.d-md-inline-block{display:inline-block !important}.d-md-block{display:block !important}.d-md-table{display:table !important}.d-md-table-row{display:table-row !important}.d-md-table-cell{display:table-cell !important}.d-md-flex{display:flex !important}.d-md-inline-flex{display:inline-flex !important}}@media(min-width:992px){.d-lg-none{display:none !important}.d-lg-inline{display:inline !important}.d-lg-inline-block{display:inline-block !important}.d-lg-block{display:block !important}.d-lg-table{display:table !important}.d-lg-table-row{display:table-row !important}.d-lg-table-cell{display:table-cell !important}.d-lg-flex{display:flex !important}.d-lg-inline-flex{display:inline-flex !important}}@media(min-width:1280px){.d-xl-none{display:none !important}.d-xl-inline{display:inline !important}.d-xl-inline-block{display:inline-block !important}.d-xl-block{display:block !important}.d-xl-table{display:table !important}.d-xl-table-row{display:table-row !important}.d-xl-table-cell{display:table-cell !important}.d-xl-flex{display:flex !important}.d-xl-inline-flex{display:inline-flex !important}}@media print{.d-print-none{display:none !important}.d-print-inline{display:inline !important}.d-print-inline-block{display:inline-block !important}.d-print-block{display:block !important}.d-print-table{display:table !important}.d-print-table-row{display:table-row !important}.d-print-table-cell{display:table-cell !important}.d-print-flex{display:flex !important}.d-print-inline-flex{display:inline-flex !important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive:before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9:before{padding-top:42.85714286%}.embed-responsive-16by9:before{padding-top:56.25%}.embed-responsive-4by3:before{padding-top:75%}.embed-responsive-1by1:before{padding-top:100%}.flex-row{flex-direction:row !important}.flex-column{flex-direction:column !important}.flex-row-reverse{flex-direction:row-reverse !important}.flex-column-reverse{flex-direction:column-reverse !important}.flex-wrap{flex-wrap:wrap !important}.flex-nowrap{flex-wrap:nowrap !important}.flex-wrap-reverse{flex-wrap:wrap-reverse !important}.justify-content-start{justify-content:flex-start !important}.justify-content-end{justify-content:flex-end !important}.justify-content-center{justify-content:center !important}.justify-content-between{justify-content:space-between !important}.justify-content-around{justify-content:space-around !important}.align-items-start{align-items:flex-start !important}.align-items-end{align-items:flex-end !important}.align-items-center{align-items:center !important}.align-items-baseline{align-items:baseline !important}.align-items-stretch{align-items:stretch !important}.align-content-start{align-content:flex-start !important}.align-content-end{align-content:flex-end !important}.align-content-center{align-content:center !important}.align-content-between{align-content:space-between !important}.align-content-around{align-content:space-around !important}.align-content-stretch{align-content:stretch !important}.align-self-auto{align-self:auto !important}.align-self-start{align-self:flex-start !important}.align-self-end{align-self:flex-end !important}.align-self-center{align-self:center !important}.align-self-baseline{align-self:baseline !important}.align-self-stretch{align-self:stretch !important}@media(min-width:576px){.flex-sm-row{flex-direction:row !important}.flex-sm-column{flex-direction:column !important}.flex-sm-row-reverse{flex-direction:row-reverse !important}.flex-sm-column-reverse{flex-direction:column-reverse !important}.flex-sm-wrap{flex-wrap:wrap !important}.flex-sm-nowrap{flex-wrap:nowrap !important}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse !important}.justify-content-sm-start{justify-content:flex-start !important}.justify-content-sm-end{justify-content:flex-end !important}.justify-content-sm-center{justify-content:center !important}.justify-content-sm-between{justify-content:space-between !important}.justify-content-sm-around{justify-content:space-around !important}.align-items-sm-start{align-items:flex-start !important}.align-items-sm-end{align-items:flex-end !important}.align-items-sm-center{align-items:center !important}.align-items-sm-baseline{align-items:baseline !important}.align-items-sm-stretch{align-items:stretch !important}.align-content-sm-start{align-content:flex-start !important}.align-content-sm-end{align-content:flex-end !important}.align-content-sm-center{align-content:center !important}.align-content-sm-between{align-content:space-between !important}.align-content-sm-around{align-content:space-around !important}.align-content-sm-stretch{align-content:stretch !important}.align-self-sm-auto{align-self:auto !important}.align-self-sm-start{align-self:flex-start !important}.align-self-sm-end{align-self:flex-end !important}.align-self-sm-center{align-self:center !important}.align-self-sm-baseline{align-self:baseline !important}.align-self-sm-stretch{align-self:stretch !important}}@media(min-width:768px){.flex-md-row{flex-direction:row !important}.flex-md-column{flex-direction:column !important}.flex-md-row-reverse{flex-direction:row-reverse !important}.flex-md-column-reverse{flex-direction:column-reverse !important}.flex-md-wrap{flex-wrap:wrap !important}.flex-md-nowrap{flex-wrap:nowrap !important}.flex-md-wrap-reverse{flex-wrap:wrap-reverse !important}.justify-content-md-start{justify-content:flex-start !important}.justify-content-md-end{justify-content:flex-end !important}.justify-content-md-center{justify-content:center !important}.justify-content-md-between{justify-content:space-between !important}.justify-content-md-around{justify-content:space-around !important}.align-items-md-start{align-items:flex-start !important}.align-items-md-end{align-items:flex-end !important}.align-items-md-center{align-items:center !important}.align-items-md-baseline{align-items:baseline !important}.align-items-md-stretch{align-items:stretch !important}.align-content-md-start{align-content:flex-start !important}.align-content-md-end{align-content:flex-end !important}.align-content-md-center{align-content:center !important}.align-content-md-between{align-content:space-between !important}.align-content-md-around{align-content:space-around !important}.align-content-md-stretch{align-content:stretch !important}.align-self-md-auto{align-self:auto !important}.align-self-md-start{align-self:flex-start !important}.align-self-md-end{align-self:flex-end !important}.align-self-md-center{align-self:center !important}.align-self-md-baseline{align-self:baseline !important}.align-self-md-stretch{align-self:stretch !important}}@media(min-width:992px){.flex-lg-row{flex-direction:row !important}.flex-lg-column{flex-direction:column !important}.flex-lg-row-reverse{flex-direction:row-reverse !important}.flex-lg-column-reverse{flex-direction:column-reverse !important}.flex-lg-wrap{flex-wrap:wrap !important}.flex-lg-nowrap{flex-wrap:nowrap !important}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse !important}.justify-content-lg-start{justify-content:flex-start !important}.justify-content-lg-end{justify-content:flex-end !important}.justify-content-lg-center{justify-content:center !important}.justify-content-lg-between{justify-content:space-between !important}.justify-content-lg-around{justify-content:space-around !important}.align-items-lg-start{align-items:flex-start !important}.align-items-lg-end{align-items:flex-end !important}.align-items-lg-center{align-items:center !important}.align-items-lg-baseline{align-items:baseline !important}.align-items-lg-stretch{align-items:stretch !important}.align-content-lg-start{align-content:flex-start !important}.align-content-lg-end{align-content:flex-end !important}.align-content-lg-center{align-content:center !important}.align-content-lg-between{align-content:space-between !important}.align-content-lg-around{align-content:space-around !important}.align-content-lg-stretch{align-content:stretch !important}.align-self-lg-auto{align-self:auto !important}.align-self-lg-start{align-self:flex-start !important}.align-self-lg-end{align-self:flex-end !important}.align-self-lg-center{align-self:center !important}.align-self-lg-baseline{align-self:baseline !important}.align-self-lg-stretch{align-self:stretch !important}}@media(min-width:1280px){.flex-xl-row{flex-direction:row !important}.flex-xl-column{flex-direction:column !important}.flex-xl-row-reverse{flex-direction:row-reverse !important}.flex-xl-column-reverse{flex-direction:column-reverse !important}.flex-xl-wrap{flex-wrap:wrap !important}.flex-xl-nowrap{flex-wrap:nowrap !important}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse !important}.justify-content-xl-start{justify-content:flex-start !important}.justify-content-xl-end{justify-content:flex-end !important}.justify-content-xl-center{justify-content:center !important}.justify-content-xl-between{justify-content:space-between !important}.justify-content-xl-around{justify-content:space-around !important}.align-items-xl-start{align-items:flex-start !important}.align-items-xl-end{align-items:flex-end !important}.align-items-xl-center{align-items:center !important}.align-items-xl-baseline{align-items:baseline !important}.align-items-xl-stretch{align-items:stretch !important}.align-content-xl-start{align-content:flex-start !important}.align-content-xl-end{align-content:flex-end !important}.align-content-xl-center{align-content:center !important}.align-content-xl-between{align-content:space-between !important}.align-content-xl-around{align-content:space-around !important}.align-content-xl-stretch{align-content:stretch !important}.align-self-xl-auto{align-self:auto !important}.align-self-xl-start{align-self:flex-start !important}.align-self-xl-end{align-self:flex-end !important}.align-self-xl-center{align-self:center !important}.align-self-xl-baseline{align-self:baseline !important}.align-self-xl-stretch{align-self:stretch !important}}.float-left{float:left !important}.float-right{float:right !important}.float-none{float:none !important}@media(min-width:576px){.float-sm-left{float:left !important}.float-sm-right{float:right !important}.float-sm-none{float:none !important}}@media(min-width:768px){.float-md-left{float:left !important}.float-md-right{float:right !important}.float-md-none{float:none !important}}@media(min-width:992px){.float-lg-left{float:left !important}.float-lg-right{float:right !important}.float-lg-none{float:none !important}}@media(min-width:1280px){.float-xl-left{float:left !important}.float-xl-right{float:right !important}.float-xl-none{float:none !important}}.position-static{position:static !important}.position-relative{position:relative !important}.position-absolute{position:absolute !important}.position-fixed{position:fixed !important}.position-sticky{position:-webkit-sticky !important;position:sticky !important}.fixed-top{top:0}.fixed-bottom,.fixed-top{position:fixed;right:0;left:0;z-index:1030}.fixed-bottom{bottom:0}@supports((position:-webkit-sticky) or(position:sticky)){.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;-webkit-clip-path:inset(50%);clip-path:inset(50%);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal;-webkit-clip-path:none;clip-path:none}.w-25{width:25% !important}.w-50{width:50% !important}.w-75{width:75% !important}.w-100{width:100% !important}.w-0{width:0 !important}.w-1{width:.25rem !important}.w-2{width:.5rem !important}.w-3{width:.75rem !important}.w-4{width:1rem !important}.w-5{width:1.5rem !important}.w-6{width:2rem !important}.w-7{width:3rem !important}.w-8{width:4rem !important}.w-9{width:6rem !important}.w-auto{width:auto !important}.h-25{height:25% !important}.h-50{height:50% !important}.h-75{height:75% !important}.h-100{height:100% !important}.h-0{height:0 !important}.h-1{height:.25rem !important}.h-2{height:.5rem !important}.h-3{height:.75rem !important}.h-4{height:1rem !important}.h-5{height:1.5rem !important}.h-6{height:2rem !important}.h-7{height:3rem !important}.h-8{height:4rem !important}.h-9{height:6rem !important}.h-auto{height:auto !important}.mw-100{max-width:100% !important}.mh-100{max-height:100% !important}.m-0{margin:0 !important}.mt-0,.my-0{margin-top:0 !important}.mr-0,.mx-0{margin-right:0 !important}.mb-0,.my-0{margin-bottom:0 !important}.ml-0,.mx-0{margin-left:0 !important}.m-1{margin:.25rem !important}.mt-1,.my-1{margin-top:.25rem !important}.mr-1,.mx-1{margin-right:.25rem !important}.mb-1,.my-1{margin-bottom:.25rem !important}.ml-1,.mx-1{margin-left:.25rem !important}.m-2{margin:.5rem !important}.mt-2,.my-2{margin-top:.5rem !important}.mr-2,.mx-2{margin-right:.5rem !important}.mb-2,.my-2{margin-bottom:.5rem !important}.ml-2,.mx-2{margin-left:.5rem !important}.m-3{margin:.75rem !important}.mt-3,.my-3{margin-top:.75rem !important}.mr-3,.mx-3{margin-right:.75rem !important}.mb-3,.my-3{margin-bottom:.75rem !important}.ml-3,.mx-3{margin-left:.75rem !important}.m-4{margin:1rem !important}.mt-4,.my-4{margin-top:1rem !important}.mr-4,.mx-4{margin-right:1rem !important}.mb-4,.my-4{margin-bottom:1rem !important}.ml-4,.mx-4{margin-left:1rem !important}.m-5{margin:1.5rem !important}.mt-5,.my-5{margin-top:1.5rem !important}.mr-5,.mx-5{margin-right:1.5rem !important}.mb-5,.my-5{margin-bottom:1.5rem !important}.ml-5,.mx-5{margin-left:1.5rem !important}.m-6{margin:2rem !important}.mt-6,.my-6{margin-top:2rem !important}.mr-6,.mx-6{margin-right:2rem !important}.mb-6,.my-6{margin-bottom:2rem !important}.ml-6,.mx-6{margin-left:2rem !important}.m-7{margin:3rem !important}.mt-7,.my-7{margin-top:3rem !important}.mr-7,.mx-7{margin-right:3rem !important}.mb-7,.my-7{margin-bottom:3rem !important}.ml-7,.mx-7{margin-left:3rem !important}.m-8{margin:4rem !important}.mt-8,.my-8{margin-top:4rem !important}.mr-8,.mx-8{margin-right:4rem !important}.mb-8,.my-8{margin-bottom:4rem !important}.ml-8,.mx-8{margin-left:4rem !important}.m-9{margin:6rem !important}.mt-9,.my-9{margin-top:6rem !important}.mr-9,.mx-9{margin-right:6rem !important}.mb-9,.my-9{margin-bottom:6rem !important}.ml-9,.mx-9{margin-left:6rem !important}.p-0{padding:0 !important}.pt-0,.py-0{padding-top:0 !important}.pr-0,.px-0{padding-right:0 !important}.pb-0,.py-0{padding-bottom:0 !important}.pl-0,.px-0{padding-left:0 !important}.p-1{padding:.25rem !important}.pt-1,.py-1{padding-top:.25rem !important}.pr-1,.px-1{padding-right:.25rem !important}.pb-1,.py-1{padding-bottom:.25rem !important}.pl-1,.px-1{padding-left:.25rem !important}.p-2{padding:.5rem !important}.pt-2,.py-2{padding-top:.5rem !important}.pr-2,.px-2{padding-right:.5rem !important}.pb-2,.py-2{padding-bottom:.5rem !important}.pl-2,.px-2{padding-left:.5rem !important}.p-3{padding:.75rem !important}.pt-3,.py-3{padding-top:.75rem !important}.pr-3,.px-3{padding-right:.75rem !important}.pb-3,.py-3{padding-bottom:.75rem !important}.pl-3,.px-3{padding-left:.75rem !important}.p-4{padding:1rem !important}.pt-4,.py-4{padding-top:1rem !important}.pr-4,.px-4{padding-right:1rem !important}.pb-4,.py-4{padding-bottom:1rem !important}.pl-4,.px-4{padding-left:1rem !important}.p-5{padding:1.5rem !important}.pt-5,.py-5{padding-top:1.5rem !important}.pr-5,.px-5{padding-right:1.5rem !important}.pb-5,.py-5{padding-bottom:1.5rem !important}.pl-5,.px-5{padding-left:1.5rem !important}.p-6{padding:2rem !important}.pt-6,.py-6{padding-top:2rem !important}.pr-6,.px-6{padding-right:2rem !important}.pb-6,.py-6{padding-bottom:2rem !important}.pl-6,.px-6{padding-left:2rem !important}.p-7{padding:3rem !important}.pt-7,.py-7{padding-top:3rem !important}.pr-7,.px-7{padding-right:3rem !important}.pb-7,.py-7{padding-bottom:3rem !important}.pl-7,.px-7{padding-left:3rem !important}.p-8{padding:4rem !important}.pt-8,.py-8{padding-top:4rem !important}.pr-8,.px-8{padding-right:4rem !important}.pb-8,.py-8{padding-bottom:4rem !important}.pl-8,.px-8{padding-left:4rem !important}.p-9{padding:6rem !important}.pt-9,.py-9{padding-top:6rem !important}.pr-9,.px-9{padding-right:6rem !important}.pb-9,.py-9{padding-bottom:6rem !important}.pl-9,.px-9{padding-left:6rem !important}.m-auto{margin:auto !important}.mt-auto,.my-auto{margin-top:auto !important}.mr-auto,.mx-auto{margin-right:auto !important}.mb-auto,.my-auto{margin-bottom:auto !important}.ml-auto,.mx-auto{margin-left:auto !important}@media(min-width:576px){.m-sm-0{margin:0 !important}.mt-sm-0,.my-sm-0{margin-top:0 !important}.mr-sm-0,.mx-sm-0{margin-right:0 !important}.mb-sm-0,.my-sm-0{margin-bottom:0 !important}.ml-sm-0,.mx-sm-0{margin-left:0 !important}.m-sm-1{margin:.25rem !important}.mt-sm-1,.my-sm-1{margin-top:.25rem !important}.mr-sm-1,.mx-sm-1{margin-right:.25rem !important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem !important}.ml-sm-1,.mx-sm-1{margin-left:.25rem !important}.m-sm-2{margin:.5rem !important}.mt-sm-2,.my-sm-2{margin-top:.5rem !important}.mr-sm-2,.mx-sm-2{margin-right:.5rem !important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem !important}.ml-sm-2,.mx-sm-2{margin-left:.5rem !important}.m-sm-3{margin:.75rem !important}.mt-sm-3,.my-sm-3{margin-top:.75rem !important}.mr-sm-3,.mx-sm-3{margin-right:.75rem !important}.mb-sm-3,.my-sm-3{margin-bottom:.75rem !important}.ml-sm-3,.mx-sm-3{margin-left:.75rem !important}.m-sm-4{margin:1rem !important}.mt-sm-4,.my-sm-4{margin-top:1rem !important}.mr-sm-4,.mx-sm-4{margin-right:1rem !important}.mb-sm-4,.my-sm-4{margin-bottom:1rem !important}.ml-sm-4,.mx-sm-4{margin-left:1rem !important}.m-sm-5{margin:1.5rem !important}.mt-sm-5,.my-sm-5{margin-top:1.5rem !important}.mr-sm-5,.mx-sm-5{margin-right:1.5rem !important}.mb-sm-5,.my-sm-5{margin-bottom:1.5rem !important}.ml-sm-5,.mx-sm-5{margin-left:1.5rem !important}.m-sm-6{margin:2rem !important}.mt-sm-6,.my-sm-6{margin-top:2rem !important}.mr-sm-6,.mx-sm-6{margin-right:2rem !important}.mb-sm-6,.my-sm-6{margin-bottom:2rem !important}.ml-sm-6,.mx-sm-6{margin-left:2rem !important}.m-sm-7{margin:3rem !important}.mt-sm-7,.my-sm-7{margin-top:3rem !important}.mr-sm-7,.mx-sm-7{margin-right:3rem !important}.mb-sm-7,.my-sm-7{margin-bottom:3rem !important}.ml-sm-7,.mx-sm-7{margin-left:3rem !important}.m-sm-8{margin:4rem !important}.mt-sm-8,.my-sm-8{margin-top:4rem !important}.mr-sm-8,.mx-sm-8{margin-right:4rem !important}.mb-sm-8,.my-sm-8{margin-bottom:4rem !important}.ml-sm-8,.mx-sm-8{margin-left:4rem !important}.m-sm-9{margin:6rem !important}.mt-sm-9,.my-sm-9{margin-top:6rem !important}.mr-sm-9,.mx-sm-9{margin-right:6rem !important}.mb-sm-9,.my-sm-9{margin-bottom:6rem !important}.ml-sm-9,.mx-sm-9{margin-left:6rem !important}.p-sm-0{padding:0 !important}.pt-sm-0,.py-sm-0{padding-top:0 !important}.pr-sm-0,.px-sm-0{padding-right:0 !important}.pb-sm-0,.py-sm-0{padding-bottom:0 !important}.pl-sm-0,.px-sm-0{padding-left:0 !important}.p-sm-1{padding:.25rem !important}.pt-sm-1,.py-sm-1{padding-top:.25rem !important}.pr-sm-1,.px-sm-1{padding-right:.25rem !important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem !important}.pl-sm-1,.px-sm-1{padding-left:.25rem !important}.p-sm-2{padding:.5rem !important}.pt-sm-2,.py-sm-2{padding-top:.5rem !important}.pr-sm-2,.px-sm-2{padding-right:.5rem !important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem !important}.pl-sm-2,.px-sm-2{padding-left:.5rem !important}.p-sm-3{padding:.75rem !important}.pt-sm-3,.py-sm-3{padding-top:.75rem !important}.pr-sm-3,.px-sm-3{padding-right:.75rem !important}.pb-sm-3,.py-sm-3{padding-bottom:.75rem !important}.pl-sm-3,.px-sm-3{padding-left:.75rem !important}.p-sm-4{padding:1rem !important}.pt-sm-4,.py-sm-4{padding-top:1rem !important}.pr-sm-4,.px-sm-4{padding-right:1rem !important}.pb-sm-4,.py-sm-4{padding-bottom:1rem !important}.pl-sm-4,.px-sm-4{padding-left:1rem !important}.p-sm-5{padding:1.5rem !important}.pt-sm-5,.py-sm-5{padding-top:1.5rem !important}.pr-sm-5,.px-sm-5{padding-right:1.5rem !important}.pb-sm-5,.py-sm-5{padding-bottom:1.5rem !important}.pl-sm-5,.px-sm-5{padding-left:1.5rem !important}.p-sm-6{padding:2rem !important}.pt-sm-6,.py-sm-6{padding-top:2rem !important}.pr-sm-6,.px-sm-6{padding-right:2rem !important}.pb-sm-6,.py-sm-6{padding-bottom:2rem !important}.pl-sm-6,.px-sm-6{padding-left:2rem !important}.p-sm-7{padding:3rem !important}.pt-sm-7,.py-sm-7{padding-top:3rem !important}.pr-sm-7,.px-sm-7{padding-right:3rem !important}.pb-sm-7,.py-sm-7{padding-bottom:3rem !important}.pl-sm-7,.px-sm-7{padding-left:3rem !important}.p-sm-8{padding:4rem !important}.pt-sm-8,.py-sm-8{padding-top:4rem !important}.pr-sm-8,.px-sm-8{padding-right:4rem !important}.pb-sm-8,.py-sm-8{padding-bottom:4rem !important}.pl-sm-8,.px-sm-8{padding-left:4rem !important}.p-sm-9{padding:6rem !important}.pt-sm-9,.py-sm-9{padding-top:6rem !important}.pr-sm-9,.px-sm-9{padding-right:6rem !important}.pb-sm-9,.py-sm-9{padding-bottom:6rem !important}.pl-sm-9,.px-sm-9{padding-left:6rem !important}.m-sm-auto{margin:auto !important}.mt-sm-auto,.my-sm-auto{margin-top:auto !important}.mr-sm-auto,.mx-sm-auto{margin-right:auto !important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto !important}.ml-sm-auto,.mx-sm-auto{margin-left:auto !important}}@media(min-width:768px){.m-md-0{margin:0 !important}.mt-md-0,.my-md-0{margin-top:0 !important}.mr-md-0,.mx-md-0{margin-right:0 !important}.mb-md-0,.my-md-0{margin-bottom:0 !important}.ml-md-0,.mx-md-0{margin-left:0 !important}.m-md-1{margin:.25rem !important}.mt-md-1,.my-md-1{margin-top:.25rem !important}.mr-md-1,.mx-md-1{margin-right:.25rem !important}.mb-md-1,.my-md-1{margin-bottom:.25rem !important}.ml-md-1,.mx-md-1{margin-left:.25rem !important}.m-md-2{margin:.5rem !important}.mt-md-2,.my-md-2{margin-top:.5rem !important}.mr-md-2,.mx-md-2{margin-right:.5rem !important}.mb-md-2,.my-md-2{margin-bottom:.5rem !important}.ml-md-2,.mx-md-2{margin-left:.5rem !important}.m-md-3{margin:.75rem !important}.mt-md-3,.my-md-3{margin-top:.75rem !important}.mr-md-3,.mx-md-3{margin-right:.75rem !important}.mb-md-3,.my-md-3{margin-bottom:.75rem !important}.ml-md-3,.mx-md-3{margin-left:.75rem !important}.m-md-4{margin:1rem !important}.mt-md-4,.my-md-4{margin-top:1rem !important}.mr-md-4,.mx-md-4{margin-right:1rem !important}.mb-md-4,.my-md-4{margin-bottom:1rem !important}.ml-md-4,.mx-md-4{margin-left:1rem !important}.m-md-5{margin:1.5rem !important}.mt-md-5,.my-md-5{margin-top:1.5rem !important}.mr-md-5,.mx-md-5{margin-right:1.5rem !important}.mb-md-5,.my-md-5{margin-bottom:1.5rem !important}.ml-md-5,.mx-md-5{margin-left:1.5rem !important}.m-md-6{margin:2rem !important}.mt-md-6,.my-md-6{margin-top:2rem !important}.mr-md-6,.mx-md-6{margin-right:2rem !important}.mb-md-6,.my-md-6{margin-bottom:2rem !important}.ml-md-6,.mx-md-6{margin-left:2rem !important}.m-md-7{margin:3rem !important}.mt-md-7,.my-md-7{margin-top:3rem !important}.mr-md-7,.mx-md-7{margin-right:3rem !important}.mb-md-7,.my-md-7{margin-bottom:3rem !important}.ml-md-7,.mx-md-7{margin-left:3rem !important}.m-md-8{margin:4rem !important}.mt-md-8,.my-md-8{margin-top:4rem !important}.mr-md-8,.mx-md-8{margin-right:4rem !important}.mb-md-8,.my-md-8{margin-bottom:4rem !important}.ml-md-8,.mx-md-8{margin-left:4rem !important}.m-md-9{margin:6rem !important}.mt-md-9,.my-md-9{margin-top:6rem !important}.mr-md-9,.mx-md-9{margin-right:6rem !important}.mb-md-9,.my-md-9{margin-bottom:6rem !important}.ml-md-9,.mx-md-9{margin-left:6rem !important}.p-md-0{padding:0 !important}.pt-md-0,.py-md-0{padding-top:0 !important}.pr-md-0,.px-md-0{padding-right:0 !important}.pb-md-0,.py-md-0{padding-bottom:0 !important}.pl-md-0,.px-md-0{padding-left:0 !important}.p-md-1{padding:.25rem !important}.pt-md-1,.py-md-1{padding-top:.25rem !important}.pr-md-1,.px-md-1{padding-right:.25rem !important}.pb-md-1,.py-md-1{padding-bottom:.25rem !important}.pl-md-1,.px-md-1{padding-left:.25rem !important}.p-md-2{padding:.5rem !important}.pt-md-2,.py-md-2{padding-top:.5rem !important}.pr-md-2,.px-md-2{padding-right:.5rem !important}.pb-md-2,.py-md-2{padding-bottom:.5rem !important}.pl-md-2,.px-md-2{padding-left:.5rem !important}.p-md-3{padding:.75rem !important}.pt-md-3,.py-md-3{padding-top:.75rem !important}.pr-md-3,.px-md-3{padding-right:.75rem !important}.pb-md-3,.py-md-3{padding-bottom:.75rem !important}.pl-md-3,.px-md-3{padding-left:.75rem !important}.p-md-4{padding:1rem !important}.pt-md-4,.py-md-4{padding-top:1rem !important}.pr-md-4,.px-md-4{padding-right:1rem !important}.pb-md-4,.py-md-4{padding-bottom:1rem !important}.pl-md-4,.px-md-4{padding-left:1rem !important}.p-md-5{padding:1.5rem !important}.pt-md-5,.py-md-5{padding-top:1.5rem !important}.pr-md-5,.px-md-5{padding-right:1.5rem !important}.pb-md-5,.py-md-5{padding-bottom:1.5rem !important}.pl-md-5,.px-md-5{padding-left:1.5rem !important}.p-md-6{padding:2rem !important}.pt-md-6,.py-md-6{padding-top:2rem !important}.pr-md-6,.px-md-6{padding-right:2rem !important}.pb-md-6,.py-md-6{padding-bottom:2rem !important}.pl-md-6,.px-md-6{padding-left:2rem !important}.p-md-7{padding:3rem !important}.pt-md-7,.py-md-7{padding-top:3rem !important}.pr-md-7,.px-md-7{padding-right:3rem !important}.pb-md-7,.py-md-7{padding-bottom:3rem !important}.pl-md-7,.px-md-7{padding-left:3rem !important}.p-md-8{padding:4rem !important}.pt-md-8,.py-md-8{padding-top:4rem !important}.pr-md-8,.px-md-8{padding-right:4rem !important}.pb-md-8,.py-md-8{padding-bottom:4rem !important}.pl-md-8,.px-md-8{padding-left:4rem !important}.p-md-9{padding:6rem !important}.pt-md-9,.py-md-9{padding-top:6rem !important}.pr-md-9,.px-md-9{padding-right:6rem !important}.pb-md-9,.py-md-9{padding-bottom:6rem !important}.pl-md-9,.px-md-9{padding-left:6rem !important}.m-md-auto{margin:auto !important}.mt-md-auto,.my-md-auto{margin-top:auto !important}.mr-md-auto,.mx-md-auto{margin-right:auto !important}.mb-md-auto,.my-md-auto{margin-bottom:auto !important}.ml-md-auto,.mx-md-auto{margin-left:auto !important}}@media(min-width:992px){.m-lg-0{margin:0 !important}.mt-lg-0,.my-lg-0{margin-top:0 !important}.mr-lg-0,.mx-lg-0{margin-right:0 !important}.mb-lg-0,.my-lg-0{margin-bottom:0 !important}.ml-lg-0,.mx-lg-0{margin-left:0 !important}.m-lg-1{margin:.25rem !important}.mt-lg-1,.my-lg-1{margin-top:.25rem !important}.mr-lg-1,.mx-lg-1{margin-right:.25rem !important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem !important}.ml-lg-1,.mx-lg-1{margin-left:.25rem !important}.m-lg-2{margin:.5rem !important}.mt-lg-2,.my-lg-2{margin-top:.5rem !important}.mr-lg-2,.mx-lg-2{margin-right:.5rem !important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem !important}.ml-lg-2,.mx-lg-2{margin-left:.5rem !important}.m-lg-3{margin:.75rem !important}.mt-lg-3,.my-lg-3{margin-top:.75rem !important}.mr-lg-3,.mx-lg-3{margin-right:.75rem !important}.mb-lg-3,.my-lg-3{margin-bottom:.75rem !important}.ml-lg-3,.mx-lg-3{margin-left:.75rem !important}.m-lg-4{margin:1rem !important}.mt-lg-4,.my-lg-4{margin-top:1rem !important}.mr-lg-4,.mx-lg-4{margin-right:1rem !important}.mb-lg-4,.my-lg-4{margin-bottom:1rem !important}.ml-lg-4,.mx-lg-4{margin-left:1rem !important}.m-lg-5{margin:1.5rem !important}.mt-lg-5,.my-lg-5{margin-top:1.5rem !important}.mr-lg-5,.mx-lg-5{margin-right:1.5rem !important}.mb-lg-5,.my-lg-5{margin-bottom:1.5rem !important}.ml-lg-5,.mx-lg-5{margin-left:1.5rem !important}.m-lg-6{margin:2rem !important}.mt-lg-6,.my-lg-6{margin-top:2rem !important}.mr-lg-6,.mx-lg-6{margin-right:2rem !important}.mb-lg-6,.my-lg-6{margin-bottom:2rem !important}.ml-lg-6,.mx-lg-6{margin-left:2rem !important}.m-lg-7{margin:3rem !important}.mt-lg-7,.my-lg-7{margin-top:3rem !important}.mr-lg-7,.mx-lg-7{margin-right:3rem !important}.mb-lg-7,.my-lg-7{margin-bottom:3rem !important}.ml-lg-7,.mx-lg-7{margin-left:3rem !important}.m-lg-8{margin:4rem !important}.mt-lg-8,.my-lg-8{margin-top:4rem !important}.mr-lg-8,.mx-lg-8{margin-right:4rem !important}.mb-lg-8,.my-lg-8{margin-bottom:4rem !important}.ml-lg-8,.mx-lg-8{margin-left:4rem !important}.m-lg-9{margin:6rem !important}.mt-lg-9,.my-lg-9{margin-top:6rem !important}.mr-lg-9,.mx-lg-9{margin-right:6rem !important}.mb-lg-9,.my-lg-9{margin-bottom:6rem !important}.ml-lg-9,.mx-lg-9{margin-left:6rem !important}.p-lg-0{padding:0 !important}.pt-lg-0,.py-lg-0{padding-top:0 !important}.pr-lg-0,.px-lg-0{padding-right:0 !important}.pb-lg-0,.py-lg-0{padding-bottom:0 !important}.pl-lg-0,.px-lg-0{padding-left:0 !important}.p-lg-1{padding:.25rem !important}.pt-lg-1,.py-lg-1{padding-top:.25rem !important}.pr-lg-1,.px-lg-1{padding-right:.25rem !important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem !important}.pl-lg-1,.px-lg-1{padding-left:.25rem !important}.p-lg-2{padding:.5rem !important}.pt-lg-2,.py-lg-2{padding-top:.5rem !important}.pr-lg-2,.px-lg-2{padding-right:.5rem !important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem !important}.pl-lg-2,.px-lg-2{padding-left:.5rem !important}.p-lg-3{padding:.75rem !important}.pt-lg-3,.py-lg-3{padding-top:.75rem !important}.pr-lg-3,.px-lg-3{padding-right:.75rem !important}.pb-lg-3,.py-lg-3{padding-bottom:.75rem !important}.pl-lg-3,.px-lg-3{padding-left:.75rem !important}.p-lg-4{padding:1rem !important}.pt-lg-4,.py-lg-4{padding-top:1rem !important}.pr-lg-4,.px-lg-4{padding-right:1rem !important}.pb-lg-4,.py-lg-4{padding-bottom:1rem !important}.pl-lg-4,.px-lg-4{padding-left:1rem !important}.p-lg-5{padding:1.5rem !important}.pt-lg-5,.py-lg-5{padding-top:1.5rem !important}.pr-lg-5,.px-lg-5{padding-right:1.5rem !important}.pb-lg-5,.py-lg-5{padding-bottom:1.5rem !important}.pl-lg-5,.px-lg-5{padding-left:1.5rem !important}.p-lg-6{padding:2rem !important}.pt-lg-6,.py-lg-6{padding-top:2rem !important}.pr-lg-6,.px-lg-6{padding-right:2rem !important}.pb-lg-6,.py-lg-6{padding-bottom:2rem !important}.pl-lg-6,.px-lg-6{padding-left:2rem !important}.p-lg-7{padding:3rem !important}.pt-lg-7,.py-lg-7{padding-top:3rem !important}.pr-lg-7,.px-lg-7{padding-right:3rem !important}.pb-lg-7,.py-lg-7{padding-bottom:3rem !important}.pl-lg-7,.px-lg-7{padding-left:3rem !important}.p-lg-8{padding:4rem !important}.pt-lg-8,.py-lg-8{padding-top:4rem !important}.pr-lg-8,.px-lg-8{padding-right:4rem !important}.pb-lg-8,.py-lg-8{padding-bottom:4rem !important}.pl-lg-8,.px-lg-8{padding-left:4rem !important}.p-lg-9{padding:6rem !important}.pt-lg-9,.py-lg-9{padding-top:6rem !important}.pr-lg-9,.px-lg-9{padding-right:6rem !important}.pb-lg-9,.py-lg-9{padding-bottom:6rem !important}.pl-lg-9,.px-lg-9{padding-left:6rem !important}.m-lg-auto{margin:auto !important}.mt-lg-auto,.my-lg-auto{margin-top:auto !important}.mr-lg-auto,.mx-lg-auto{margin-right:auto !important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto !important}.ml-lg-auto,.mx-lg-auto{margin-left:auto !important}}@media(min-width:1280px){.m-xl-0{margin:0 !important}.mt-xl-0,.my-xl-0{margin-top:0 !important}.mr-xl-0,.mx-xl-0{margin-right:0 !important}.mb-xl-0,.my-xl-0{margin-bottom:0 !important}.ml-xl-0,.mx-xl-0{margin-left:0 !important}.m-xl-1{margin:.25rem !important}.mt-xl-1,.my-xl-1{margin-top:.25rem !important}.mr-xl-1,.mx-xl-1{margin-right:.25rem !important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem !important}.ml-xl-1,.mx-xl-1{margin-left:.25rem !important}.m-xl-2{margin:.5rem !important}.mt-xl-2,.my-xl-2{margin-top:.5rem !important}.mr-xl-2,.mx-xl-2{margin-right:.5rem !important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem !important}.ml-xl-2,.mx-xl-2{margin-left:.5rem !important}.m-xl-3{margin:.75rem !important}.mt-xl-3,.my-xl-3{margin-top:.75rem !important}.mr-xl-3,.mx-xl-3{margin-right:.75rem !important}.mb-xl-3,.my-xl-3{margin-bottom:.75rem !important}.ml-xl-3,.mx-xl-3{margin-left:.75rem !important}.m-xl-4{margin:1rem !important}.mt-xl-4,.my-xl-4{margin-top:1rem !important}.mr-xl-4,.mx-xl-4{margin-right:1rem !important}.mb-xl-4,.my-xl-4{margin-bottom:1rem !important}.ml-xl-4,.mx-xl-4{margin-left:1rem !important}.m-xl-5{margin:1.5rem !important}.mt-xl-5,.my-xl-5{margin-top:1.5rem !important}.mr-xl-5,.mx-xl-5{margin-right:1.5rem !important}.mb-xl-5,.my-xl-5{margin-bottom:1.5rem !important}.ml-xl-5,.mx-xl-5{margin-left:1.5rem !important}.m-xl-6{margin:2rem !important}.mt-xl-6,.my-xl-6{margin-top:2rem !important}.mr-xl-6,.mx-xl-6{margin-right:2rem !important}.mb-xl-6,.my-xl-6{margin-bottom:2rem !important}.ml-xl-6,.mx-xl-6{margin-left:2rem !important}.m-xl-7{margin:3rem !important}.mt-xl-7,.my-xl-7{margin-top:3rem !important}.mr-xl-7,.mx-xl-7{margin-right:3rem !important}.mb-xl-7,.my-xl-7{margin-bottom:3rem !important}.ml-xl-7,.mx-xl-7{margin-left:3rem !important}.m-xl-8{margin:4rem !important}.mt-xl-8,.my-xl-8{margin-top:4rem !important}.mr-xl-8,.mx-xl-8{margin-right:4rem !important}.mb-xl-8,.my-xl-8{margin-bottom:4rem !important}.ml-xl-8,.mx-xl-8{margin-left:4rem !important}.m-xl-9{margin:6rem !important}.mt-xl-9,.my-xl-9{margin-top:6rem !important}.mr-xl-9,.mx-xl-9{margin-right:6rem !important}.mb-xl-9,.my-xl-9{margin-bottom:6rem !important}.ml-xl-9,.mx-xl-9{margin-left:6rem !important}.p-xl-0{padding:0 !important}.pt-xl-0,.py-xl-0{padding-top:0 !important}.pr-xl-0,.px-xl-0{padding-right:0 !important}.pb-xl-0,.py-xl-0{padding-bottom:0 !important}.pl-xl-0,.px-xl-0{padding-left:0 !important}.p-xl-1{padding:.25rem !important}.pt-xl-1,.py-xl-1{padding-top:.25rem !important}.pr-xl-1,.px-xl-1{padding-right:.25rem !important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem !important}.pl-xl-1,.px-xl-1{padding-left:.25rem !important}.p-xl-2{padding:.5rem !important}.pt-xl-2,.py-xl-2{padding-top:.5rem !important}.pr-xl-2,.px-xl-2{padding-right:.5rem !important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem !important}.pl-xl-2,.px-xl-2{padding-left:.5rem !important}.p-xl-3{padding:.75rem !important}.pt-xl-3,.py-xl-3{padding-top:.75rem !important}.pr-xl-3,.px-xl-3{padding-right:.75rem !important}.pb-xl-3,.py-xl-3{padding-bottom:.75rem !important}.pl-xl-3,.px-xl-3{padding-left:.75rem !important}.p-xl-4{padding:1rem !important}.pt-xl-4,.py-xl-4{padding-top:1rem !important}.pr-xl-4,.px-xl-4{padding-right:1rem !important}.pb-xl-4,.py-xl-4{padding-bottom:1rem !important}.pl-xl-4,.px-xl-4{padding-left:1rem !important}.p-xl-5{padding:1.5rem !important}.pt-xl-5,.py-xl-5{padding-top:1.5rem !important}.pr-xl-5,.px-xl-5{padding-right:1.5rem !important}.pb-xl-5,.py-xl-5{padding-bottom:1.5rem !important}.pl-xl-5,.px-xl-5{padding-left:1.5rem !important}.p-xl-6{padding:2rem !important}.pt-xl-6,.py-xl-6{padding-top:2rem !important}.pr-xl-6,.px-xl-6{padding-right:2rem !important}.pb-xl-6,.py-xl-6{padding-bottom:2rem !important}.pl-xl-6,.px-xl-6{padding-left:2rem !important}.p-xl-7{padding:3rem !important}.pt-xl-7,.py-xl-7{padding-top:3rem !important}.pr-xl-7,.px-xl-7{padding-right:3rem !important}.pb-xl-7,.py-xl-7{padding-bottom:3rem !important}.pl-xl-7,.px-xl-7{padding-left:3rem !important}.p-xl-8{padding:4rem !important}.pt-xl-8,.py-xl-8{padding-top:4rem !important}.pr-xl-8,.px-xl-8{padding-right:4rem !important}.pb-xl-8,.py-xl-8{padding-bottom:4rem !important}.pl-xl-8,.px-xl-8{padding-left:4rem !important}.p-xl-9{padding:6rem !important}.pt-xl-9,.py-xl-9{padding-top:6rem !important}.pr-xl-9,.px-xl-9{padding-right:6rem !important}.pb-xl-9,.py-xl-9{padding-bottom:6rem !important}.pl-xl-9,.px-xl-9{padding-left:6rem !important}.m-xl-auto{margin:auto !important}.mt-xl-auto,.my-xl-auto{margin-top:auto !important}.mr-xl-auto,.mx-xl-auto{margin-right:auto !important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto !important}.ml-xl-auto,.mx-xl-auto{margin-left:auto !important}}.text-justify{text-align:justify !important}.text-nowrap{white-space:nowrap !important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left !important}.text-right{text-align:right !important}.text-center{text-align:center !important}@media(min-width:576px){.text-sm-left{text-align:left !important}.text-sm-right{text-align:right !important}.text-sm-center{text-align:center !important}}@media(min-width:768px){.text-md-left{text-align:left !important}.text-md-right{text-align:right !important}.text-md-center{text-align:center !important}}@media(min-width:992px){.text-lg-left{text-align:left !important}.text-lg-right{text-align:right !important}.text-lg-center{text-align:center !important}}@media(min-width:1280px){.text-xl-left{text-align:left !important}.text-xl-right{text-align:right !important}.text-xl-center{text-align:center !important}}.text-lowercase{text-transform:lowercase !important}.text-uppercase{text-transform:uppercase !important}.text-capitalize{text-transform:capitalize !important}.font-weight-light{font-weight:300 !important}.font-weight-normal{font-weight:400 !important}.font-weight-bold{font-weight:700 !important}.font-italic{font-style:italic !important}.text-primary{color:#467fcf !important}a.text-primary:focus,a.text-primary:hover{color:#2f66b3 !important}.text-secondary{color:#868e96 !important}a.text-secondary:focus,a.text-secondary:hover{color:#6c757d !important}.text-success{color:#5eba00 !important}a.text-success:focus,a.text-success:hover{color:#448700 !important}.text-info{color:#45aaf2 !important}a.text-info:focus,a.text-info:hover{color:#1594ef !important}.text-warning{color:#f1c40f !important}a.text-warning:focus,a.text-warning:hover{color:#c29d0b !important}.text-danger{color:#cd201f !important}a.text-danger:focus,a.text-danger:hover{color:#a11918 !important}.text-light{color:#f8f9fa !important}a.text-light:focus,a.text-light:hover{color:#dae0e5 !important}.text-dark{color:#343a40 !important}a.text-dark:focus,a.text-dark:hover{color:#1d2124 !important}.text-muted{color:#9aa0ac !important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:initial;border:0}.visible{visibility:visible !important}.invisible{visibility:hidden !important}@media print{*,:after,:before{text-shadow:none !important;box-shadow:none !important}a:not(.btn){text-decoration:underline}abbr[title]:after{content:" (" attr(title) ")"}pre{white-space:pre-wrap !important}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}.container,body{min-width:992px !important}.navbar{display:none}.badge{border:1px solid #000}.table,.text-wrap table{border-collapse:collapse !important}.table td,.table th,.text-wrap table td,.text-wrap table th{background-color:#fff !important}.table-bordered td,.table-bordered th,.text-wrap table td,.text-wrap table th{border:1px solid #ddd !important}}html{font-size:16px}body,html{height:100%;direction:ltr}body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;-webkit-tap-highlight-color:transparent;-webkit-text-size-adjust:none;touch-action:manipulation;-webkit-font-feature-settings:"liga" 0;font-feature-settings:"liga" 0;overflow-y:scroll;position:relative}@media print{body{background:0}}body ::-webkit-scrollbar{width:6px;height:6px;transition:background .3s}body ::-webkit-scrollbar-thumb{background:#ced4da}body :hover::-webkit-scrollbar-thumb{background:#adb5bd}.lead{line-height:1.4}a{-webkit-text-decoration-skip:ink;text-decoration-skip:ink}.h1 a,.h2 a,.h3 a,.h4 a,.h5 a,.h6 a,h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{color:inherit}b,strong{font-weight:600}blockquote,ol,p,ul{margin-bottom:1em}blockquote{font-style:italic;color:#6e7687;padding-left:2rem;border-left:2px solid rgba(0,40,100,.12)}blockquote p{margin-bottom:1rem}blockquote cite{display:block;text-align:right}blockquote cite:before{content:"\2014 "}code{background:rgba(0,0,0,.025);border:1px solid rgba(0,0,0,.05);border-radius:3px;padding:3px}pre code{padding:0;border-radius:0;border:0;background:0}hr{margin-top:2rem;margin-bottom:2rem}pre{color:#343a40;padding:1rem;overflow:auto;font-size:85%;line-height:1.45;background-color:#f8fafc;border-radius:3px;-moz-tab-size:4;tab-size:4;text-shadow:0 1px #fff;-webkit-hyphens:none;-ms-hyphens:none;hyphens:none}img{max-width:100%}.text-wrap{font-size:1rem;line-height:1.66}.text-wrap>:first-child{margin-top:0}.text-wrap>:last-child{margin-bottom:0}.text-wrap>h1,.text-wrap>h2,.text-wrap>h3,.text-wrap>h4,.text-wrap>h5,.text-wrap>h6{margin-top:1em}.section-nav{background-color:#f8f9fa;margin:1rem 0;padding:.5rem 1rem;border:1px solid rgba(0,40,100,.12);border-radius:3px;list-style:none}.section-nav:before{content:"Table of contents:";display:block;font-weight:600}@media print{.container{max-width:none}}.row-cards>.col,.row-cards>[class*=col-]{display:flex;flex-direction:column}.row-deck>.col,.row-deck>[class*=col-]{display:flex;align-items:stretch}.row-deck>.col .card,.row-deck>[class*=col-] .card{flex:1 1 auto}.col-text{max-width:48rem}.col-login{max-width:24rem}.gutters-0{margin-right:0;margin-left:0}.gutters-0>.col,.gutters-0>[class*=col-]{padding-right:0;padding-left:0}.gutters-0 .card{margin-bottom:0}.gutters-xs{margin-right:-.25rem;margin-left:-.25rem}.gutters-xs>.col,.gutters-xs>[class*=col-]{padding-right:.25rem;padding-left:.25rem}.gutters-xs .card{margin-bottom:.5rem}.gutters-sm{margin-right:-.5rem;margin-left:-.5rem}.gutters-sm>.col,.gutters-sm>[class*=col-]{padding-right:.5rem;padding-left:.5rem}.gutters-sm .card{margin-bottom:1rem}.gutters-lg{margin-right:-1rem;margin-left:-1rem}.gutters-lg>.col,.gutters-lg>[class*=col-]{padding-right:1rem;padding-left:1rem}.gutters-lg .card{margin-bottom:2rem}.gutters-xl{margin-right:-1.5rem;margin-left:-1.5rem}.gutters-xl>.col,.gutters-xl>[class*=col-]{padding-right:1.5rem;padding-left:1.5rem}.gutters-xl .card{margin-bottom:3rem}.page{display:flex;flex-direction:column;justify-content:center;min-height:100%}body.fixed-header .page{padding-top:4.5rem}@media(min-width:1600px){body.aside-opened .page{margin-right:22rem}}.page-main{flex:1 1 auto}.page-content{margin:.75rem 0}@media(min-width:768px){.page-content{margin:1.5rem 0}}.page-header{display:flex;align-items:center;margin:1.5rem 0;flex-wrap:wrap}.page-title{margin:0;font-size:1.5rem;font-weight:400;line-height:2.5rem}.page-title-icon{color:#9aa0ac;font-size:1.25rem}.page-subtitle{font-size:.8125rem;color:#6e7687;margin-left:2rem}.page-subtitle a{color:inherit}.page-options{margin-left:auto}.page-breadcrumb{flex-basis:100%}.page-description{margin:.25rem 0 0;color:#6e7687}.page-description a{color:inherit}.page-single{flex:1 1 auto;display:flex;align-items:center;justify-content:center;padding:1rem 0}.content-heading{font-weight:400;margin:2rem 0 1.5rem;font-size:1.25rem;line-height:1.25}.content-heading:first-child{margin-top:0}.aside{position:fixed;top:0;right:0;bottom:0;width:22rem;background:#fff;border-left:1px solid rgba(0,40,100,.12);display:flex;flex-direction:column;z-index:100;visibility:hidden;box-shadow:0 0 5px 2px rgba(0,0,0,.05)}@media(min-width:1600px){body.aside-opened .aside{visibility:visible}}.aside-body{padding:1.5rem;flex:1 1 auto;overflow:auto}.aside-footer{padding:1rem 1.5rem;border-top:1px solid rgba(0,40,100,.12)}.aside-header{padding:1rem 1.5rem}.aside-header,.header{border-bottom:1px solid rgba(0,40,100,.12)}.header{padding-top:.75rem;padding-bottom:.75rem;background:#fff}body.fixed-header .header{position:fixed;top:0;left:0;right:0;z-index:1030}@media print{.header{display:none}}.header .dropdown-menu{margin-top:.75rem}.nav-unread{position:absolute;top:.25rem;right:.25rem;background:#cd201f;width:.5rem;height:.5rem;border-radius:50%}.header-brand{color:inherit;margin-right:1rem;font-size:1.25rem;white-space:nowrap;font-weight:600;padding:0;transition:opacity .3s;line-height:2rem}.header-brand:hover{opacity:.8;color:inherit;text-decoration:none}.header-brand-img{height:2rem;line-height:2rem;vertical-align:bottom;margin-right:.5rem;width:auto}.header-avatar{vertical-align:bottom;border-radius:50%}.header-avatar,.header-btn{width:2rem;height:2rem;display:inline-block}.header-btn{line-height:2rem;text-align:center;font-size:1rem}.header-btn.has-new{position:relative}.header-btn.has-new:before{content:"";width:6px;height:6px;background:#cd201f;position:absolute;top:4px;right:4px;border-radius:50%}.header-toggler{width:2rem;height:2rem;position:relative;color:#9aa0ac}.header-toggler:hover{color:#6e7687}.header-toggler-icon{position:absolute;width:1rem;height:2px;color:inherit;background:currentColor;border-radius:3px;top:50%;left:50%;margin:-2px 0 0 -.5rem;box-shadow:0 5px currentColor,0 -5px currentColor}.footer{background:#fff;border-top:1px solid rgba(0,40,100,.12);font-size:.875rem;padding:1.25rem 0;color:#9aa0ac}.footer a:not(.btn){color:#6e7687}@media print{.footer{display:none}}.bg-blue-lightest{background-color:#edf2fa !important}a.bg-blue-lightest:focus,a.bg-blue-lightest:hover,button.bg-blue-lightest:focus,button.bg-blue-lightest:hover{background-color:#c5d5ef !important}.bg-blue-lighter{background-color:#c8d9f1 !important}a.bg-blue-lighter:focus,a.bg-blue-lighter:hover,button.bg-blue-lighter:focus,button.bg-blue-lighter:hover{background-color:#9fbde7 !important}.bg-blue-light{background-color:#7ea5dd !important}a.bg-blue-light:focus,a.bg-blue-light:hover,button.bg-blue-light:focus,button.bg-blue-light:hover{background-color:#5689d2 !important}.bg-blue-dark{background-color:#3866a6 !important}a.bg-blue-dark:focus,a.bg-blue-dark:hover,button.bg-blue-dark:focus,button.bg-blue-dark:hover{background-color:#2b4f80 !important}.bg-blue-darker{background-color:#1c3353 !important}a.bg-blue-darker:focus,a.bg-blue-darker:hover,button.bg-blue-darker:focus,button.bg-blue-darker:hover{background-color:#0f1c2d !important}.bg-blue-darkest{background-color:#0e1929 !important}a.bg-blue-darkest:focus,a.bg-blue-darkest:hover,button.bg-blue-darkest:focus,button.bg-blue-darkest:hover{background-color:#010203 !important}.bg-indigo-lightest{background-color:#f0f1fa !important}a.bg-indigo-lightest:focus,a.bg-indigo-lightest:hover,button.bg-indigo-lightest:focus,button.bg-indigo-lightest:hover{background-color:#cacded !important}.bg-indigo-lighter{background-color:#d1d5f0 !important}a.bg-indigo-lighter:focus,a.bg-indigo-lighter:hover,button.bg-indigo-lighter:focus,button.bg-indigo-lighter:hover{background-color:#abb2e3 !important}.bg-indigo-light{background-color:#939edc !important}a.bg-indigo-light:focus,a.bg-indigo-light:hover,button.bg-indigo-light:focus,button.bg-indigo-light:hover{background-color:#6c7bd0 !important}.bg-indigo-dark{background-color:#515da4 !important}a.bg-indigo-dark:focus,a.bg-indigo-dark:hover,button.bg-indigo-dark:focus,button.bg-indigo-dark:hover{background-color:#404a82 !important}.bg-indigo-darker{background-color:#282e52 !important}a.bg-indigo-darker:focus,a.bg-indigo-darker:hover,button.bg-indigo-darker:focus,button.bg-indigo-darker:hover{background-color:#171b30 !important}.bg-indigo-darkest{background-color:#141729 !important}a.bg-indigo-darkest:focus,a.bg-indigo-darkest:hover,button.bg-indigo-darkest:focus,button.bg-indigo-darkest:hover{background-color:#030407 !important}.bg-purple-lightest{background-color:#f6effd !important}a.bg-purple-lightest:focus,a.bg-purple-lightest:hover,button.bg-purple-lightest:focus,button.bg-purple-lightest:hover{background-color:#ddc2f7 !important}.bg-purple-lighter{background-color:#e4cff9 !important}a.bg-purple-lighter:focus,a.bg-purple-lighter:hover,button.bg-purple-lighter:focus,button.bg-purple-lighter:hover{background-color:#cba2f3 !important}.bg-purple-light{background-color:#c08ef0 !important}a.bg-purple-light:focus,a.bg-purple-light:hover,button.bg-purple-light:focus,button.bg-purple-light:hover{background-color:#a761ea !important}.bg-purple-dark{background-color:#844bbb !important}a.bg-purple-dark:focus,a.bg-purple-dark:hover,button.bg-purple-dark:focus,button.bg-purple-dark:hover{background-color:#6a3a99 !important}.bg-purple-darker{background-color:#42265e !important}a.bg-purple-darker:focus,a.bg-purple-darker:hover,button.bg-purple-darker:focus,button.bg-purple-darker:hover{background-color:#29173a !important}.bg-purple-darkest{background-color:#21132f !important}a.bg-purple-darkest:focus,a.bg-purple-darkest:hover,button.bg-purple-darkest:focus,button.bg-purple-darkest:hover{background-color:#08040b !important}.bg-pink-lightest{background-color:#fef0f5 !important}a.bg-pink-lightest:focus,a.bg-pink-lightest:hover,button.bg-pink-lightest:focus,button.bg-pink-lightest:hover{background-color:#fbc0d5 !important}.bg-pink-lighter{background-color:#fcd3e1 !important}a.bg-pink-lighter:focus,a.bg-pink-lighter:hover,button.bg-pink-lighter:focus,button.bg-pink-lighter:hover{background-color:#f9a3c0 !important}.bg-pink-light{background-color:#f999b9 !important}a.bg-pink-light:focus,a.bg-pink-light:hover,button.bg-pink-light:focus,button.bg-pink-light:hover{background-color:#f66998 !important}.bg-pink-dark{background-color:#c5577c !important}a.bg-pink-dark:focus,a.bg-pink-dark:hover,button.bg-pink-dark:focus,button.bg-pink-dark:hover{background-color:#ad3c62 !important}.bg-pink-darker{background-color:#622c3e !important}a.bg-pink-darker:focus,a.bg-pink-darker:hover,button.bg-pink-darker:focus,button.bg-pink-darker:hover{background-color:#3f1c28 !important}.bg-pink-darkest{background-color:#31161f !important}a.bg-pink-darkest:focus,a.bg-pink-darkest:hover,button.bg-pink-darkest:focus,button.bg-pink-darkest:hover{background-color:#0e0609 !important}.bg-red-lightest{background-color:#fae9e9 !important}a.bg-red-lightest:focus,a.bg-red-lightest:hover,button.bg-red-lightest:focus,button.bg-red-lightest:hover{background-color:#f1bfbf !important}.bg-red-lighter{background-color:#f0bcbc !important}a.bg-red-lighter:focus,a.bg-red-lighter:hover,button.bg-red-lighter:focus,button.bg-red-lighter:hover{background-color:#e79292 !important}.bg-red-light{background-color:#dc6362 !important}a.bg-red-light:focus,a.bg-red-light:hover,button.bg-red-light:focus,button.bg-red-light:hover{background-color:#d33a38 !important}.bg-red-dark{background-color:#a41a19 !important}a.bg-red-dark:focus,a.bg-red-dark:hover,button.bg-red-dark:focus,button.bg-red-dark:hover{background-color:#781312 !important}.bg-red-darker{background-color:#520d0c !important}a.bg-red-darker:focus,a.bg-red-darker:hover,button.bg-red-darker:focus,button.bg-red-darker:hover{background-color:#260605 !important}.bg-red-darkest{background-color:#290606 !important}a.bg-red-darkest:focus,a.bg-red-darkest:hover,button.bg-red-darkest:focus,button.bg-red-darkest:hover{background-color:#000 !important}.bg-orange-lightest{background-color:#fff5ec !important}a.bg-orange-lightest:focus,a.bg-orange-lightest:hover,button.bg-orange-lightest:focus,button.bg-orange-lightest:hover{background-color:#ffdab9 !important}.bg-orange-lighter{background-color:#fee0c7 !important}a.bg-orange-lighter:focus,a.bg-orange-lighter:hover,button.bg-orange-lighter:focus,button.bg-orange-lighter:hover{background-color:#fdc495 !important}.bg-orange-light{background-color:#feb67c !important}a.bg-orange-light:focus,a.bg-orange-light:hover,button.bg-orange-light:focus,button.bg-orange-light:hover{background-color:#fe9a49 !important}.bg-orange-dark{background-color:#ca7836 !important}a.bg-orange-dark:focus,a.bg-orange-dark:hover,button.bg-orange-dark:focus,button.bg-orange-dark:hover{background-color:#a2602b !important}.bg-orange-darker{background-color:#653c1b !important}a.bg-orange-darker:focus,a.bg-orange-darker:hover,button.bg-orange-darker:focus,button.bg-orange-darker:hover{background-color:#3d2410 !important}.bg-orange-darkest{background-color:#331e0e !important}a.bg-orange-darkest:focus,a.bg-orange-darkest:hover,button.bg-orange-darkest:focus,button.bg-orange-darkest:hover{background-color:#0b0603 !important}.bg-yellow-lightest{background-color:#fef9e7 !important}a.bg-yellow-lightest:focus,a.bg-yellow-lightest:hover,button.bg-yellow-lightest:focus,button.bg-yellow-lightest:hover{background-color:#fcedb6 !important}.bg-yellow-lighter{background-color:#fbedb7 !important}a.bg-yellow-lighter:focus,a.bg-yellow-lighter:hover,button.bg-yellow-lighter:focus,button.bg-yellow-lighter:hover{background-color:#f8e187 !important}.bg-yellow-light{background-color:#f5d657 !important}a.bg-yellow-light:focus,a.bg-yellow-light:hover,button.bg-yellow-light:focus,button.bg-yellow-light:hover{background-color:#f2ca27 !important}.bg-yellow-dark{background-color:#c19d0c !important}a.bg-yellow-dark:focus,a.bg-yellow-dark:hover,button.bg-yellow-dark:focus,button.bg-yellow-dark:hover{background-color:#917609 !important}.bg-yellow-darker{background-color:#604e06 !important}.bg-yellow-darkest,a.bg-yellow-darker:focus,a.bg-yellow-darker:hover,button.bg-yellow-darker:focus,button.bg-yellow-darker:hover{background-color:#302703 !important}a.bg-yellow-darkest:focus,a.bg-yellow-darkest:hover,button.bg-yellow-darkest:focus,button.bg-yellow-darkest:hover{background-color:#000 !important}.bg-green-lightest{background-color:#eff8e6 !important}a.bg-green-lightest:focus,a.bg-green-lightest:hover,button.bg-green-lightest:focus,button.bg-green-lightest:hover{background-color:#d6edbe !important}.bg-green-lighter{background-color:#cfeab3 !important}a.bg-green-lighter:focus,a.bg-green-lighter:hover,button.bg-green-lighter:focus,button.bg-green-lighter:hover{background-color:#b6df8b !important}.bg-green-light{background-color:#8ecf4d !important}a.bg-green-light:focus,a.bg-green-light:hover,button.bg-green-light:focus,button.bg-green-light:hover{background-color:#75b831 !important}.bg-green-dark{background-color:#4b9500 !important}a.bg-green-dark:focus,a.bg-green-dark:hover,button.bg-green-dark:focus,button.bg-green-dark:hover{background-color:#316200 !important}.bg-green-darker{background-color:#264a00 !important}a.bg-green-darker:focus,a.bg-green-darker:hover,button.bg-green-darker:focus,button.bg-green-darker:hover{background-color:#0c1700 !important}.bg-green-darkest{background-color:#132500 !important}a.bg-green-darkest:focus,a.bg-green-darkest:hover,button.bg-green-darkest:focus,button.bg-green-darkest:hover{background-color:#000 !important}.bg-teal-lightest{background-color:#eafaf8 !important}a.bg-teal-lightest:focus,a.bg-teal-lightest:hover,button.bg-teal-lightest:focus,button.bg-teal-lightest:hover{background-color:#c1f0ea !important}.bg-teal-lighter{background-color:#bfefea !important}a.bg-teal-lighter:focus,a.bg-teal-lighter:hover,button.bg-teal-lighter:focus,button.bg-teal-lighter:hover{background-color:#96e5dd !important}.bg-teal-light{background-color:#6bdbcf !important}a.bg-teal-light:focus,a.bg-teal-light:hover,button.bg-teal-light:focus,button.bg-teal-light:hover{background-color:#42d1c2 !important}.bg-teal-dark{background-color:#22a295 !important}a.bg-teal-dark:focus,a.bg-teal-dark:hover,button.bg-teal-dark:focus,button.bg-teal-dark:hover{background-color:#19786e !important}.bg-teal-darker{background-color:#11514a !important}a.bg-teal-darker:focus,a.bg-teal-darker:hover,button.bg-teal-darker:focus,button.bg-teal-darker:hover{background-color:#082723 !important}.bg-teal-darkest{background-color:#092925 !important}a.bg-teal-darkest:focus,a.bg-teal-darkest:hover,button.bg-teal-darkest:focus,button.bg-teal-darkest:hover{background-color:#000 !important}.bg-cyan-lightest{background-color:#e8f6f8 !important}a.bg-cyan-lightest:focus,a.bg-cyan-lightest:hover,button.bg-cyan-lightest:focus,button.bg-cyan-lightest:hover{background-color:#c1e7ec !important}.bg-cyan-lighter{background-color:#b9e3ea !important}a.bg-cyan-lighter:focus,a.bg-cyan-lighter:hover,button.bg-cyan-lighter:focus,button.bg-cyan-lighter:hover{background-color:#92d3de !important}.bg-cyan-light{background-color:#5dbecd !important}a.bg-cyan-light:focus,a.bg-cyan-light:hover,button.bg-cyan-light:focus,button.bg-cyan-light:hover{background-color:#3aabbd !important}.bg-cyan-dark{background-color:#128293 !important}a.bg-cyan-dark:focus,a.bg-cyan-dark:hover,button.bg-cyan-dark:focus,button.bg-cyan-dark:hover{background-color:#0c5a66 !important}.bg-cyan-darker{background-color:#09414a !important}a.bg-cyan-darker:focus,a.bg-cyan-darker:hover,button.bg-cyan-darker:focus,button.bg-cyan-darker:hover{background-color:#03191d !important}.bg-cyan-darkest{background-color:#052025 !important}a.bg-cyan-darkest:focus,a.bg-cyan-darkest:hover,button.bg-cyan-darkest:focus,button.bg-cyan-darkest:hover{background-color:#000 !important}.bg-white-lightest{background-color:#fff !important}a.bg-white-lightest:focus,a.bg-white-lightest:hover,button.bg-white-lightest:focus,button.bg-white-lightest:hover{background-color:#e6e5e5 !important}.bg-white-lighter{background-color:#fff !important}a.bg-white-lighter:focus,a.bg-white-lighter:hover,button.bg-white-lighter:focus,button.bg-white-lighter:hover{background-color:#e6e5e5 !important}.bg-white-light{background-color:#fff !important}a.bg-white-light:focus,a.bg-white-light:hover,button.bg-white-light:focus,button.bg-white-light:hover{background-color:#e6e5e5 !important}.bg-white-dark{background-color:#ccc !important}a.bg-white-dark:focus,a.bg-white-dark:hover,button.bg-white-dark:focus,button.bg-white-dark:hover{background-color:#b3b2b2 !important}.bg-white-darker{background-color:#666 !important}a.bg-white-darker:focus,a.bg-white-darker:hover,button.bg-white-darker:focus,button.bg-white-darker:hover{background-color:#4d4c4c !important}.bg-white-darkest{background-color:#333 !important}a.bg-white-darkest:focus,a.bg-white-darkest:hover,button.bg-white-darkest:focus,button.bg-white-darkest:hover{background-color:#1a1919 !important}.bg-gray-lightest{background-color:#f3f4f5 !important}a.bg-gray-lightest:focus,a.bg-gray-lightest:hover,button.bg-gray-lightest:focus,button.bg-gray-lightest:hover{background-color:#d7dbde !important}.bg-gray-lighter{background-color:#dbdde0 !important}a.bg-gray-lighter:focus,a.bg-gray-lighter:hover,button.bg-gray-lighter:focus,button.bg-gray-lighter:hover{background-color:#c0c3c8 !important}.bg-gray-light{background-color:#aab0b6 !important}a.bg-gray-light:focus,a.bg-gray-light:hover,button.bg-gray-light:focus,button.bg-gray-light:hover{background-color:#8f979e !important}.bg-gray-dark{background-color:#6b7278 !important}a.bg-gray-dark:focus,a.bg-gray-dark:hover,button.bg-gray-dark:focus,button.bg-gray-dark:hover{background-color:#53585d !important}.bg-gray-darker{background-color:#36393c !important}a.bg-gray-darker:focus,a.bg-gray-darker:hover,button.bg-gray-darker:focus,button.bg-gray-darker:hover{background-color:#1e2021 !important}.bg-gray-darkest{background-color:#1b1c1e !important}a.bg-gray-darkest:focus,a.bg-gray-darkest:hover,button.bg-gray-darkest:focus,button.bg-gray-darkest:hover{background-color:#030303 !important}.bg-gray-dark-lightest{background-color:#ebebec !important}a.bg-gray-dark-lightest:focus,a.bg-gray-dark-lightest:hover,button.bg-gray-dark-lightest:focus,button.bg-gray-dark-lightest:hover{background-color:#d1d1d3 !important}.bg-gray-dark-lighter{background-color:#c2c4c6 !important}a.bg-gray-dark-lighter:focus,a.bg-gray-dark-lighter:hover,button.bg-gray-dark-lighter:focus,button.bg-gray-dark-lighter:hover{background-color:#a8abad !important}.bg-gray-dark-light{background-color:#717579 !important}a.bg-gray-dark-light:focus,a.bg-gray-dark-light:hover,button.bg-gray-dark-light:focus,button.bg-gray-dark-light:hover{background-color:#585c5f !important}.bg-gray-dark-dark{background-color:#2a2e33 !important}a.bg-gray-dark-dark:focus,a.bg-gray-dark-dark:hover,button.bg-gray-dark-dark:focus,button.bg-gray-dark-dark:hover{background-color:#131517 !important}.bg-gray-dark-darker{background-color:#15171a !important}a.bg-gray-dark-darker:focus,a.bg-gray-dark-darker:hover,button.bg-gray-dark-darker:focus,button.bg-gray-dark-darker:hover{background-color:#000 !important}.bg-gray-dark-darkest{background-color:#0a0c0d !important}a.bg-gray-dark-darkest:focus,a.bg-gray-dark-darkest:hover,button.bg-gray-dark-darkest:focus,button.bg-gray-dark-darkest:hover{background-color:#000 !important}.bg-azure-lightest{background-color:#ecf7fe !important}a.bg-azure-lightest:focus,a.bg-azure-lightest:hover,button.bg-azure-lightest:focus,button.bg-azure-lightest:hover{background-color:#bce3fb !important}.bg-azure-lighter{background-color:#c7e6fb !important}a.bg-azure-lighter:focus,a.bg-azure-lighter:hover,button.bg-azure-lighter:focus,button.bg-azure-lighter:hover{background-color:#97d1f8 !important}.bg-azure-light{background-color:#7dc4f6 !important}a.bg-azure-light:focus,a.bg-azure-light:hover,button.bg-azure-light:focus,button.bg-azure-light:hover{background-color:#4daef3 !important}.bg-azure-dark{background-color:#3788c2 !important}a.bg-azure-dark:focus,a.bg-azure-dark:hover,button.bg-azure-dark:focus,button.bg-azure-dark:hover{background-color:#2c6c9a !important}.bg-azure-darker{background-color:#1c4461 !important}a.bg-azure-darker:focus,a.bg-azure-darker:hover,button.bg-azure-darker:focus,button.bg-azure-darker:hover{background-color:#112839 !important}.bg-azure-darkest{background-color:#0e2230 !important}a.bg-azure-darkest:focus,a.bg-azure-darkest:hover,button.bg-azure-darkest:focus,button.bg-azure-darkest:hover{background-color:#020609 !important}.bg-lime-lightest{background-color:#f2fbeb !important}a.bg-lime-lightest:focus,a.bg-lime-lightest:hover,button.bg-lime-lightest:focus,button.bg-lime-lightest:hover{background-color:#d6f3c1 !important}.bg-lime-lighter{background-color:#d7f2c2 !important}a.bg-lime-lighter:focus,a.bg-lime-lighter:hover,button.bg-lime-lighter:focus,button.bg-lime-lighter:hover{background-color:#bbe998 !important}.bg-lime-light{background-color:#a3e072 !important}a.bg-lime-light:focus,a.bg-lime-light:hover,button.bg-lime-light:focus,button.bg-lime-light:hover{background-color:#88d748 !important}.bg-lime-dark{background-color:#62a82a !important}a.bg-lime-dark:focus,a.bg-lime-dark:hover,button.bg-lime-dark:focus,button.bg-lime-dark:hover{background-color:#4a7f20 !important}.bg-lime-darker{background-color:#315415 !important}a.bg-lime-darker:focus,a.bg-lime-darker:hover,button.bg-lime-darker:focus,button.bg-lime-darker:hover{background-color:#192b0b !important}.bg-lime-darkest{background-color:#192a0b !important}a.bg-lime-darkest:focus,a.bg-lime-darkest:hover,button.bg-lime-darkest:focus,button.bg-lime-darkest:hover{background-color:#010200 !important}.display-1 i,.display-2 i,.display-3 i,.display-4 i{vertical-align:initial;font-size:.815em}.text-inherit{color:inherit !important}.text-default{color:#495057 !important}.text-muted-dark{color:#6e7687 !important}.tracking-tight{letter-spacing:-.05em !important}.tracking-normal{letter-spacing:0 !important}.tracking-wide{letter-spacing:.05em !important}.leading-none{line-height:1 !important}.leading-tight{line-height:1.25 !important}.leading-normal{line-height:1.5 !important}.leading-loose{line-height:2 !important}.bg-blue{background-color:#467fcf !important}a.bg-blue:focus,a.bg-blue:hover,button.bg-blue:focus,button.bg-blue:hover{background-color:#2f66b3 !important}.text-blue{color:#467fcf !important}.bg-indigo{background-color:#6574cd !important}a.bg-indigo:focus,a.bg-indigo:hover,button.bg-indigo:focus,button.bg-indigo:hover{background-color:#3f51c1 !important}.text-indigo{color:#6574cd !important}.bg-purple{background-color:#a55eea !important}a.bg-purple:focus,a.bg-purple:hover,button.bg-purple:focus,button.bg-purple:hover{background-color:#8c31e4 !important}.text-purple{color:#a55eea !important}.bg-pink{background-color:#f66d9b !important}a.bg-pink:focus,a.bg-pink:hover,button.bg-pink:focus,button.bg-pink:hover{background-color:#f33d7a !important}.text-pink{color:#f66d9b !important}.bg-red{background-color:#cd201f !important}a.bg-red:focus,a.bg-red:hover,button.bg-red:focus,button.bg-red:hover{background-color:#a11918 !important}.text-red{color:#cd201f !important}.bg-orange{background-color:#fd9644 !important}a.bg-orange:focus,a.bg-orange:hover,button.bg-orange:focus,button.bg-orange:hover{background-color:#fc7a12 !important}.text-orange{color:#fd9644 !important}.bg-yellow{background-color:#f1c40f !important}a.bg-yellow:focus,a.bg-yellow:hover,button.bg-yellow:focus,button.bg-yellow:hover{background-color:#c29d0b !important}.text-yellow{color:#f1c40f !important}.bg-green{background-color:#5eba00 !important}a.bg-green:focus,a.bg-green:hover,button.bg-green:focus,button.bg-green:hover{background-color:#448700 !important}.text-green{color:#5eba00 !important}.bg-teal{background-color:#2bcbba !important}a.bg-teal:focus,a.bg-teal:hover,button.bg-teal:focus,button.bg-teal:hover{background-color:#22a193 !important}.text-teal{color:#2bcbba !important}.bg-cyan{background-color:#17a2b8 !important}a.bg-cyan:focus,a.bg-cyan:hover,button.bg-cyan:focus,button.bg-cyan:hover{background-color:#117a8b !important}.text-cyan{color:#17a2b8 !important}.bg-white{background-color:#fff !important}a.bg-white:focus,a.bg-white:hover,button.bg-white:focus,button.bg-white:hover{background-color:#e6e5e5 !important}.text-white{color:#fff !important}.bg-gray{background-color:#868e96 !important}a.bg-gray:focus,a.bg-gray:hover,button.bg-gray:focus,button.bg-gray:hover{background-color:#6c757d !important}.text-gray{color:#868e96 !important}.bg-gray-dark{background-color:#343a40 !important}a.bg-gray-dark:focus,a.bg-gray-dark:hover,button.bg-gray-dark:focus,button.bg-gray-dark:hover{background-color:#1d2124 !important}.text-gray-dark{color:#343a40 !important}.bg-azure{background-color:#45aaf2 !important}a.bg-azure:focus,a.bg-azure:hover,button.bg-azure:focus,button.bg-azure:hover{background-color:#1594ef !important}.text-azure{color:#45aaf2 !important}.bg-lime{background-color:#7bd235 !important}a.bg-lime:focus,a.bg-lime:hover,button.bg-lime:focus,button.bg-lime:hover{background-color:#63ad27 !important}.text-lime{color:#7bd235 !important}.icon{color:#9aa0ac !important}.icon i{vertical-align:-1px}a.icon{text-decoration:none;cursor:pointer}a.icon:hover{color:#495057 !important}.o-auto{overflow:auto !important}.o-hidden{overflow:hidden !important}.shadow{box-shadow:0 1px 2px 0 rgba(0,0,0,.05) !important}.shadow-none{box-shadow:none !important}.nav-item,.nav-link{padding:0 .75rem;min-width:2rem;transition:color .3s;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:pointer;display:flex;align-items:center}.nav-item .badge,.nav-link .badge{position:absolute;top:0;right:0;padding:.2rem .25rem;min-width:1rem}.nav-tabs{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;color:#9aa0ac;margin:0 -.75rem}.nav-tabs .nav-link{border:0;color:inherit;border-bottom:1px solid transparent;margin-bottom:-1px;transition:border-color .3s;font-weight:400;padding:1rem 0}.nav-tabs .nav-link:hover:not(.disabled){border-color:#6e7687;color:#6e7687}.nav-tabs .nav-link.active{border-color:#467fcf;color:#467fcf;background:transparent}.nav-tabs .nav-link.disabled{opacity:.4;cursor:default;pointer-events:none}.nav-tabs .nav-item{margin-bottom:0;position:relative}.nav-tabs .nav-item i{margin-right:.25rem;line-height:1;font-size:.875rem;width:.875rem;vertical-align:initial;display:inline-block}.nav-tabs .nav-item:hover .nav-submenu{display:block}.nav-tabs .nav-submenu{display:none;position:absolute;background:#fff;border:1px solid rgba(0,40,100,.12);border-top:0;z-index:10;box-shadow:0 1px 2px 0 rgba(0,0,0,.05);min-width:10rem;border-radius:0 0 3px 3px}.nav-tabs .nav-submenu .nav-item{display:block;padding:.5rem 1rem;color:#9aa0ac;margin:0 !important;cursor:pointer;transition:background .3s}.nav-tabs .nav-submenu .nav-item.active{color:#467fcf}.nav-tabs .nav-submenu .nav-item:hover{color:#6e7687;text-decoration:none;background:rgba(0,0,0,.024)}.btn{cursor:pointer;font-weight:600;letter-spacing:.03em;font-size:.8125rem;min-width:2.375rem}.btn i{font-size:1rem;vertical-align:-2px}.btn-icon{padding-left:.5rem;padding-right:.5rem;text-align:center}.btn-secondary{color:#495057;background-color:#fff;border-color:rgba(0,40,100,.12);box-shadow:0 1px 1px 0 rgba(0,0,0,.05)}.btn-secondary:hover{color:#495057;background-color:#f6f6f6;border-color:rgba(0,20,49,.12)}.btn-secondary.focus,.btn-secondary:focus{box-shadow:0 0 0 2px rgba(0,40,100,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#495057;background-color:#fff;border-color:rgba(0,40,100,.12)}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#495057;background-color:#e6e5e5;border-color:rgba(0,15,36,.12)}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(0,40,100,.5)}.btn-pill{border-radius:10rem;padding-left:1.5em;padding-right:1.5em}.btn-square{border-radius:0}.btn-facebook{color:#fff;background-color:#3b5998;border-color:#3b5998}.btn-facebook:hover{color:#fff;background-color:#30497c;border-color:#2d4373}.btn-facebook.focus,.btn-facebook:focus{box-shadow:0 0 0 2px rgba(59,89,152,.5)}.btn-facebook.disabled,.btn-facebook:disabled{color:#fff;background-color:#3b5998;border-color:#3b5998}.btn-facebook:not(:disabled):not(.disabled).active,.btn-facebook:not(:disabled):not(.disabled):active,.show>.btn-facebook.dropdown-toggle{color:#fff;background-color:#2d4373;border-color:#293e6a}.btn-facebook:not(:disabled):not(.disabled).active:focus,.btn-facebook:not(:disabled):not(.disabled):active:focus,.show>.btn-facebook.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(59,89,152,.5)}.btn-twitter{color:#fff;background-color:#1da1f2;border-color:#1da1f2}.btn-twitter:hover{color:#fff;background-color:#0d8ddc;border-color:#0c85d0}.btn-twitter.focus,.btn-twitter:focus{box-shadow:0 0 0 2px rgba(29,161,242,.5)}.btn-twitter.disabled,.btn-twitter:disabled{color:#fff;background-color:#1da1f2;border-color:#1da1f2}.btn-twitter:not(:disabled):not(.disabled).active,.btn-twitter:not(:disabled):not(.disabled):active,.show>.btn-twitter.dropdown-toggle{color:#fff;background-color:#0c85d0;border-color:#0b7ec4}.btn-twitter:not(:disabled):not(.disabled).active:focus,.btn-twitter:not(:disabled):not(.disabled):active:focus,.show>.btn-twitter.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(29,161,242,.5)}.btn-google{color:#fff;background-color:#dc4e41;border-color:#dc4e41}.btn-google:hover{color:#fff;background-color:#d03526;border-color:#c63224}.btn-google.focus,.btn-google:focus{box-shadow:0 0 0 2px rgba(220,78,65,.5)}.btn-google.disabled,.btn-google:disabled{color:#fff;background-color:#dc4e41;border-color:#dc4e41}.btn-google:not(:disabled):not(.disabled).active,.btn-google:not(:disabled):not(.disabled):active,.show>.btn-google.dropdown-toggle{color:#fff;background-color:#c63224;border-color:#bb2f22}.btn-google:not(:disabled):not(.disabled).active:focus,.btn-google:not(:disabled):not(.disabled):active:focus,.show>.btn-google.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(220,78,65,.5)}.btn-youtube{color:#fff;background-color:red;border-color:red}.btn-youtube:hover{color:#fff;background-color:#d90000;border-color:#c00}.btn-youtube.focus,.btn-youtube:focus{box-shadow:0 0 0 2px rgba(255,0,0,.5)}.btn-youtube.disabled,.btn-youtube:disabled{color:#fff;background-color:red;border-color:red}.btn-youtube:not(:disabled):not(.disabled).active,.btn-youtube:not(:disabled):not(.disabled):active,.show>.btn-youtube.dropdown-toggle{color:#fff;background-color:#c00;border-color:#bf0000}.btn-youtube:not(:disabled):not(.disabled).active:focus,.btn-youtube:not(:disabled):not(.disabled):active:focus,.show>.btn-youtube.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(255,0,0,.5)}.btn-vimeo{color:#fff;background-color:#1ab7ea;border-color:#1ab7ea}.btn-vimeo:hover{color:#fff;background-color:#139ecb;border-color:#1295bf}.btn-vimeo.focus,.btn-vimeo:focus{box-shadow:0 0 0 2px rgba(26,183,234,.5)}.btn-vimeo.disabled,.btn-vimeo:disabled{color:#fff;background-color:#1ab7ea;border-color:#1ab7ea}.btn-vimeo:not(:disabled):not(.disabled).active,.btn-vimeo:not(:disabled):not(.disabled):active,.show>.btn-vimeo.dropdown-toggle{color:#fff;background-color:#1295bf;border-color:#108cb4}.btn-vimeo:not(:disabled):not(.disabled).active:focus,.btn-vimeo:not(:disabled):not(.disabled):active:focus,.show>.btn-vimeo.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(26,183,234,.5)}.btn-dribbble{color:#fff;background-color:#ea4c89;border-color:#ea4c89}.btn-dribbble:hover{color:#fff;background-color:#e62a72;border-color:#e51e6b}.btn-dribbble.focus,.btn-dribbble:focus{box-shadow:0 0 0 2px rgba(234,76,137,.5)}.btn-dribbble.disabled,.btn-dribbble:disabled{color:#fff;background-color:#ea4c89;border-color:#ea4c89}.btn-dribbble:not(:disabled):not(.disabled).active,.btn-dribbble:not(:disabled):not(.disabled):active,.show>.btn-dribbble.dropdown-toggle{color:#fff;background-color:#e51e6b;border-color:#dc1a65}.btn-dribbble:not(:disabled):not(.disabled).active:focus,.btn-dribbble:not(:disabled):not(.disabled):active:focus,.show>.btn-dribbble.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(234,76,137,.5)}.btn-github{color:#fff;background-color:#181717;border-color:#181717}.btn-github:hover{color:#fff;background-color:#040404;border-color:#000}.btn-github.focus,.btn-github:focus{box-shadow:0 0 0 2px rgba(24,23,23,.5)}.btn-github.disabled,.btn-github:disabled{color:#fff;background-color:#181717;border-color:#181717}.btn-github:not(:disabled):not(.disabled).active,.btn-github:not(:disabled):not(.disabled):active,.show>.btn-github.dropdown-toggle{color:#fff;background-color:#000;border-color:#000}.btn-github:not(:disabled):not(.disabled).active:focus,.btn-github:not(:disabled):not(.disabled):active:focus,.show>.btn-github.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(24,23,23,.5)}.btn-instagram{color:#fff;background-color:#e4405f;border-color:#e4405f}.btn-instagram:hover{color:#fff;background-color:#de1f44;border-color:#d31e40}.btn-instagram.focus,.btn-instagram:focus{box-shadow:0 0 0 2px rgba(228,64,95,.5)}.btn-instagram.disabled,.btn-instagram:disabled{color:#fff;background-color:#e4405f;border-color:#e4405f}.btn-instagram:not(:disabled):not(.disabled).active,.btn-instagram:not(:disabled):not(.disabled):active,.show>.btn-instagram.dropdown-toggle{color:#fff;background-color:#d31e40;border-color:#c81c3d}.btn-instagram:not(:disabled):not(.disabled).active:focus,.btn-instagram:not(:disabled):not(.disabled):active:focus,.show>.btn-instagram.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(228,64,95,.5)}.btn-pinterest{color:#fff;background-color:#bd081c;border-color:#bd081c}.btn-pinterest:hover{color:#fff;background-color:#980617;border-color:#8c0615}.btn-pinterest.focus,.btn-pinterest:focus{box-shadow:0 0 0 2px rgba(189,8,28,.5)}.btn-pinterest.disabled,.btn-pinterest:disabled{color:#fff;background-color:#bd081c;border-color:#bd081c}.btn-pinterest:not(:disabled):not(.disabled).active,.btn-pinterest:not(:disabled):not(.disabled):active,.show>.btn-pinterest.dropdown-toggle{color:#fff;background-color:#8c0615;border-color:#800513}.btn-pinterest:not(:disabled):not(.disabled).active:focus,.btn-pinterest:not(:disabled):not(.disabled):active:focus,.show>.btn-pinterest.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(189,8,28,.5)}.btn-vk{color:#fff;background-color:#6383a8;border-color:#6383a8}.btn-vk:hover{color:#fff;background-color:#527093;border-color:#4d6a8b}.btn-vk.focus,.btn-vk:focus{box-shadow:0 0 0 2px rgba(99,131,168,.5)}.btn-vk.disabled,.btn-vk:disabled{color:#fff;background-color:#6383a8;border-color:#6383a8}.btn-vk:not(:disabled):not(.disabled).active,.btn-vk:not(:disabled):not(.disabled):active,.show>.btn-vk.dropdown-toggle{color:#fff;background-color:#4d6a8b;border-color:#496482}.btn-vk:not(:disabled):not(.disabled).active:focus,.btn-vk:not(:disabled):not(.disabled):active:focus,.show>.btn-vk.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(99,131,168,.5)}.btn-rss{color:#fff;background-color:orange;border-color:orange}.btn-rss:hover{color:#fff;background-color:#d98c00;border-color:#cc8400}.btn-rss.focus,.btn-rss:focus{box-shadow:0 0 0 2px rgba(255,165,0,.5)}.btn-rss.disabled,.btn-rss:disabled{color:#fff;background-color:orange;border-color:orange}.btn-rss:not(:disabled):not(.disabled).active,.btn-rss:not(:disabled):not(.disabled):active,.show>.btn-rss.dropdown-toggle{color:#fff;background-color:#cc8400;border-color:#bf7c00}.btn-rss:not(:disabled):not(.disabled).active:focus,.btn-rss:not(:disabled):not(.disabled):active:focus,.show>.btn-rss.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(255,165,0,.5)}.btn-flickr{color:#fff;background-color:#0063dc;border-color:#0063dc}.btn-flickr:hover{color:#fff;background-color:#0052b6;border-color:#004ca9}.btn-flickr.focus,.btn-flickr:focus{box-shadow:0 0 0 2px rgba(0,99,220,.5)}.btn-flickr.disabled,.btn-flickr:disabled{color:#fff;background-color:#0063dc;border-color:#0063dc}.btn-flickr:not(:disabled):not(.disabled).active,.btn-flickr:not(:disabled):not(.disabled):active,.show>.btn-flickr.dropdown-toggle{color:#fff;background-color:#004ca9;border-color:#00469c}.btn-flickr:not(:disabled):not(.disabled).active:focus,.btn-flickr:not(:disabled):not(.disabled):active:focus,.show>.btn-flickr.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(0,99,220,.5)}.btn-bitbucket{color:#fff;background-color:#0052cc;border-color:#0052cc}.btn-bitbucket:hover{color:#fff;background-color:#0043a6;border-color:#003e99}.btn-bitbucket.focus,.btn-bitbucket:focus{box-shadow:0 0 0 2px rgba(0,82,204,.5)}.btn-bitbucket.disabled,.btn-bitbucket:disabled{color:#fff;background-color:#0052cc;border-color:#0052cc}.btn-bitbucket:not(:disabled):not(.disabled).active,.btn-bitbucket:not(:disabled):not(.disabled):active,.show>.btn-bitbucket.dropdown-toggle{color:#fff;background-color:#003e99;border-color:#00388c}.btn-bitbucket:not(:disabled):not(.disabled).active:focus,.btn-bitbucket:not(:disabled):not(.disabled):active:focus,.show>.btn-bitbucket.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(0,82,204,.5)}.btn-blue{color:#fff;background-color:#467fcf;border-color:#467fcf}.btn-blue:hover{color:#fff;background-color:#316cbe;border-color:#2f66b3}.btn-blue.focus,.btn-blue:focus{box-shadow:0 0 0 2px rgba(70,127,207,.5)}.btn-blue.disabled,.btn-blue:disabled{color:#fff;background-color:#467fcf;border-color:#467fcf}.btn-blue:not(:disabled):not(.disabled).active,.btn-blue:not(:disabled):not(.disabled):active,.show>.btn-blue.dropdown-toggle{color:#fff;background-color:#2f66b3;border-color:#2c60a9}.btn-blue:not(:disabled):not(.disabled).active:focus,.btn-blue:not(:disabled):not(.disabled):active:focus,.show>.btn-blue.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(70,127,207,.5)}.btn-indigo{color:#fff;background-color:#6574cd;border-color:#6574cd}.btn-indigo:hover{color:#fff;background-color:#485ac4;border-color:#3f51c1}.btn-indigo.focus,.btn-indigo:focus{box-shadow:0 0 0 2px rgba(101,116,205,.5)}.btn-indigo.disabled,.btn-indigo:disabled{color:#fff;background-color:#6574cd;border-color:#6574cd}.btn-indigo:not(:disabled):not(.disabled).active,.btn-indigo:not(:disabled):not(.disabled):active,.show>.btn-indigo.dropdown-toggle{color:#fff;background-color:#3f51c1;border-color:#3b4db7}.btn-indigo:not(:disabled):not(.disabled).active:focus,.btn-indigo:not(:disabled):not(.disabled):active:focus,.show>.btn-indigo.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(101,116,205,.5)}.btn-purple{color:#fff;background-color:#a55eea;border-color:#a55eea}.btn-purple:hover{color:#fff;background-color:#923ce6;border-color:#8c31e4}.btn-purple.focus,.btn-purple:focus{box-shadow:0 0 0 2px rgba(165,94,234,.5)}.btn-purple.disabled,.btn-purple:disabled{color:#fff;background-color:#a55eea;border-color:#a55eea}.btn-purple:not(:disabled):not(.disabled).active,.btn-purple:not(:disabled):not(.disabled):active,.show>.btn-purple.dropdown-toggle{color:#fff;background-color:#8c31e4;border-color:#8526e3}.btn-purple:not(:disabled):not(.disabled).active:focus,.btn-purple:not(:disabled):not(.disabled):active:focus,.show>.btn-purple.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(165,94,234,.5)}.btn-pink{color:#fff;background-color:#f66d9b;border-color:#f66d9b}.btn-pink:hover{color:#fff;background-color:#f44982;border-color:#f33d7a}.btn-pink.focus,.btn-pink:focus{box-shadow:0 0 0 2px rgba(246,109,155,.5)}.btn-pink.disabled,.btn-pink:disabled{color:#fff;background-color:#f66d9b;border-color:#f66d9b}.btn-pink:not(:disabled):not(.disabled).active,.btn-pink:not(:disabled):not(.disabled):active,.show>.btn-pink.dropdown-toggle{color:#fff;background-color:#f33d7a;border-color:#f23172}.btn-pink:not(:disabled):not(.disabled).active:focus,.btn-pink:not(:disabled):not(.disabled):active:focus,.show>.btn-pink.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(246,109,155,.5)}.btn-red{color:#fff;background-color:#cd201f;border-color:#cd201f}.btn-red:hover{color:#fff;background-color:#ac1b1a;border-color:#a11918}.btn-red.focus,.btn-red:focus{box-shadow:0 0 0 2px rgba(205,32,31,.5)}.btn-red.disabled,.btn-red:disabled{color:#fff;background-color:#cd201f;border-color:#cd201f}.btn-red:not(:disabled):not(.disabled).active,.btn-red:not(:disabled):not(.disabled):active,.show>.btn-red.dropdown-toggle{color:#fff;background-color:#a11918;border-color:#961717}.btn-red:not(:disabled):not(.disabled).active:focus,.btn-red:not(:disabled):not(.disabled):active:focus,.show>.btn-red.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(205,32,31,.5)}.btn-orange{color:#fff;background-color:#fd9644;border-color:#fd9644}.btn-orange:hover{color:#fff;background-color:#fd811e;border-color:#fc7a12}.btn-orange.focus,.btn-orange:focus{box-shadow:0 0 0 2px rgba(253,150,68,.5)}.btn-orange.disabled,.btn-orange:disabled{color:#fff;background-color:#fd9644;border-color:#fd9644}.btn-orange:not(:disabled):not(.disabled).active,.btn-orange:not(:disabled):not(.disabled):active,.show>.btn-orange.dropdown-toggle{color:#fff;background-color:#fc7a12;border-color:#fc7305}.btn-orange:not(:disabled):not(.disabled).active:focus,.btn-orange:not(:disabled):not(.disabled):active:focus,.show>.btn-orange.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(253,150,68,.5)}.btn-yellow{color:#fff;background-color:#f1c40f;border-color:#f1c40f}.btn-yellow:hover{color:#fff;background-color:#cea70c;border-color:#c29d0b}.btn-yellow.focus,.btn-yellow:focus{box-shadow:0 0 0 2px rgba(241,196,15,.5)}.btn-yellow.disabled,.btn-yellow:disabled{color:#fff;background-color:#f1c40f;border-color:#f1c40f}.btn-yellow:not(:disabled):not(.disabled).active,.btn-yellow:not(:disabled):not(.disabled):active,.show>.btn-yellow.dropdown-toggle{color:#fff;background-color:#c29d0b;border-color:#b6940b}.btn-yellow:not(:disabled):not(.disabled).active:focus,.btn-yellow:not(:disabled):not(.disabled):active:focus,.show>.btn-yellow.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(241,196,15,.5)}.btn-green{color:#fff;background-color:#5eba00;border-color:#5eba00}.btn-green:hover{color:#fff;background-color:#4b9400;border-color:#448700}.btn-green.focus,.btn-green:focus{box-shadow:0 0 0 2px rgba(94,186,0,.5)}.btn-green.disabled,.btn-green:disabled{color:#fff;background-color:#5eba00;border-color:#5eba00}.btn-green:not(:disabled):not(.disabled).active,.btn-green:not(:disabled):not(.disabled):active,.show>.btn-green.dropdown-toggle{color:#fff;background-color:#448700;border-color:#3e7a00}.btn-green:not(:disabled):not(.disabled).active:focus,.btn-green:not(:disabled):not(.disabled):active:focus,.show>.btn-green.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(94,186,0,.5)}.btn-teal{color:#fff;background-color:#2bcbba;border-color:#2bcbba}.btn-teal:hover{color:#fff;background-color:#24ab9d;border-color:#22a193}.btn-teal.focus,.btn-teal:focus{box-shadow:0 0 0 2px rgba(43,203,186,.5)}.btn-teal.disabled,.btn-teal:disabled{color:#fff;background-color:#2bcbba;border-color:#2bcbba}.btn-teal:not(:disabled):not(.disabled).active,.btn-teal:not(:disabled):not(.disabled):active,.show>.btn-teal.dropdown-toggle{color:#fff;background-color:#22a193;border-color:#20968a}.btn-teal:not(:disabled):not(.disabled).active:focus,.btn-teal:not(:disabled):not(.disabled):active:focus,.show>.btn-teal.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(43,203,186,.5)}.btn-cyan{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-cyan:hover{color:#fff;background-color:#138496;border-color:#117a8b}.btn-cyan.focus,.btn-cyan:focus{box-shadow:0 0 0 2px rgba(23,162,184,.5)}.btn-cyan.disabled,.btn-cyan:disabled{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-cyan:not(:disabled):not(.disabled).active,.btn-cyan:not(:disabled):not(.disabled):active,.show>.btn-cyan.dropdown-toggle{color:#fff;background-color:#117a8b;border-color:#10707f}.btn-cyan:not(:disabled):not(.disabled).active:focus,.btn-cyan:not(:disabled):not(.disabled):active:focus,.show>.btn-cyan.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(23,162,184,.5)}.btn-white{color:#495057;background-color:#fff;border-color:#fff}.btn-white:hover{color:#495057;background-color:#ececec;border-color:#e6e5e5}.btn-white.focus,.btn-white:focus{box-shadow:0 0 0 2px hsla(0,0%,100%,.5)}.btn-white.disabled,.btn-white:disabled{color:#495057;background-color:#fff;border-color:#fff}.btn-white:not(:disabled):not(.disabled).active,.btn-white:not(:disabled):not(.disabled):active,.show>.btn-white.dropdown-toggle{color:#495057;background-color:#e6e5e5;border-color:#dfdfdf}.btn-white:not(:disabled):not(.disabled).active:focus,.btn-white:not(:disabled):not(.disabled):active:focus,.show>.btn-white.dropdown-toggle:focus{box-shadow:0 0 0 2px hsla(0,0%,100%,.5)}.btn-gray{color:#fff;background-color:#868e96;border-color:#868e96}.btn-gray:hover{color:#fff;background-color:#727b84;border-color:#6c757d}.btn-gray.focus,.btn-gray:focus{box-shadow:0 0 0 2px rgba(134,142,150,.5)}.btn-gray.disabled,.btn-gray:disabled{color:#fff;background-color:#868e96;border-color:#868e96}.btn-gray:not(:disabled):not(.disabled).active,.btn-gray:not(:disabled):not(.disabled):active,.show>.btn-gray.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#666e76}.btn-gray:not(:disabled):not(.disabled).active:focus,.btn-gray:not(:disabled):not(.disabled):active:focus,.show>.btn-gray.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(134,142,150,.5)}.btn-gray-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-gray-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-gray-dark.focus,.btn-gray-dark:focus{box-shadow:0 0 0 2px rgba(52,58,64,.5)}.btn-gray-dark.disabled,.btn-gray-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-gray-dark:not(:disabled):not(.disabled).active,.btn-gray-dark:not(:disabled):not(.disabled):active,.show>.btn-gray-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-gray-dark:not(:disabled):not(.disabled).active:focus,.btn-gray-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-gray-dark.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(52,58,64,.5)}.btn-azure{color:#fff;background-color:#45aaf2;border-color:#45aaf2}.btn-azure:hover{color:#fff;background-color:#219af0;border-color:#1594ef}.btn-azure.focus,.btn-azure:focus{box-shadow:0 0 0 2px rgba(69,170,242,.5)}.btn-azure.disabled,.btn-azure:disabled{color:#fff;background-color:#45aaf2;border-color:#45aaf2}.btn-azure:not(:disabled):not(.disabled).active,.btn-azure:not(:disabled):not(.disabled):active,.show>.btn-azure.dropdown-toggle{color:#fff;background-color:#1594ef;border-color:#108ee7}.btn-azure:not(:disabled):not(.disabled).active:focus,.btn-azure:not(:disabled):not(.disabled):active:focus,.show>.btn-azure.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(69,170,242,.5)}.btn-lime{color:#fff;background-color:#7bd235;border-color:#7bd235}.btn-lime:hover{color:#fff;background-color:#69b829;border-color:#63ad27}.btn-lime.focus,.btn-lime:focus{box-shadow:0 0 0 2px rgba(123,210,53,.5)}.btn-lime.disabled,.btn-lime:disabled{color:#fff;background-color:#7bd235;border-color:#7bd235}.btn-lime:not(:disabled):not(.disabled).active,.btn-lime:not(:disabled):not(.disabled):active,.show>.btn-lime.dropdown-toggle{color:#fff;background-color:#63ad27;border-color:#5da324}.btn-lime:not(:disabled):not(.disabled).active:focus,.btn-lime:not(:disabled):not(.disabled):active:focus,.show>.btn-lime.dropdown-toggle:focus{box-shadow:0 0 0 2px rgba(123,210,53,.5)}.btn-option{background:transparent;color:#9aa0ac}.btn-option:hover{color:#6e7687}.btn-option:focus{box-shadow:none;color:#6e7687}.btn-group-sm>.btn,.btn-sm{font-size:.75rem;min-width:1.625rem}.btn-group-lg>.btn,.btn-lg{font-size:1rem;min-width:2.75rem;font-weight:400}.btn-list{margin-bottom:-.5rem;font-size:0}.btn-list>.btn,.btn-list>.dropdown{margin-bottom:.5rem}.btn-list>.btn:not(:last-child),.btn-list>.dropdown:not(:last-child){margin-right:.5rem}.btn-loading{color:transparent !important;pointer-events:none;position:relative}.btn-loading:after{content:"";-webkit-animation:loader .5s linear infinite;animation:loader .5s linear infinite;border:2px solid #fff;border-radius:50%;border-right-color:transparent !important;border-top-color:transparent !important;display:block;height:1.4em;width:1.4em;position:absolute;left:calc(50% - .7em);top:calc(50% - .7em);-webkit-transform-origin:center;transform-origin:center;position:absolute !important}.btn-group-sm>.btn-loading.btn:after,.btn-loading.btn-sm:after{height:1em;width:1em;left:calc(50% - .5em);top:calc(50% - .5em)}.btn-loading.btn-secondary:after{border-color:#495057}.alert{font-size:.9375rem}.alert-icon{padding-left:3rem}.alert-icon>i{color:inherit !important;font-size:1rem;position:absolute;top:1rem;left:1rem}.alert-avatar{padding-left:3.75rem}.alert-avatar .avatar{position:absolute;top:.5rem;left:.75rem}.close{font-size:1rem;line-height:1.5;transition:color .3s}.close:before{content:"\EA00";font-family:feather}.badge{color:#fff}.badge-default{background:#e9ecef;color:#868e96}.table thead th,.text-wrap table thead th{border-top:0;border-bottom-width:1px;padding-top:.5rem;padding-bottom:.5rem}.table th,.text-wrap table th{color:#9aa0ac;text-transform:uppercase;font-size:.875rem;font-weight:400}.table-md td,.table-md th{padding:.5rem}.table-vcenter td,.table-vcenter th{vertical-align:middle}.table-center td,.table-center th{text-align:center}.table-striped tbody tr:nth-of-type(odd){background:transparent}.table-striped tbody tr:nth-of-type(2n){background-color:rgba(0,0,0,.02)}.table-calendar{margin:0 0 .75rem}.table-calendar td,.table-calendar th{border:0;text-align:center;padding:0 !important;width:14.28571429%;line-height:2.5rem}.table-calendar td{border-top:0}.table-calendar-link{line-height:2rem;min-width:calc(2rem + 2px);display:inline-block;border-radius:3px;background:#f8f9fa;color:#495057;font-weight:600;transition:background .3s,color .3s;position:relative}.table-calendar-link:before{content:"";width:4px;height:4px;position:absolute;left:.25rem;top:.25rem;border-radius:50px;background:#467fcf}.table-calendar-link:hover{color:#fff;text-decoration:none;background:#467fcf;transition:background .3s}.table-calendar-link:hover:before{background:#fff}.table-header{cursor:pointer;transition:color .3s}.table-header:hover{color:#495057 !important}.table-header:after{content:"\F0DC";font-family:FontAwesome;display:inline-block;margin-left:.5rem;font-size:.75rem}.table-header-asc{color:#495057 !important}.table-header-asc:after{content:"\F0DE"}.table-header-desc{color:#495057 !important}.table-header-desc:after{content:"\F0DD"}.page-breadcrumb{background:0;padding:0;margin:1rem 0 0;font-size:.875rem}@media(min-width:768px){.page-breadcrumb{margin:-.5rem 0 0}}.page-breadcrumb .breadcrumb-item{color:#9aa0ac}.page-breadcrumb .breadcrumb-item.active{color:#6e7687}.pagination-simple .page-item .page-link{background:0;border:0}.pagination-simple .page-item.active .page-link{color:#495057;font-weight:700}.pagination-pager .page-prev{margin-right:auto}.pagination-pager .page-next{margin-left:auto}.page-total-text{margin-right:1rem;align-self:center;color:#6e7687}.card{box-shadow:0 1px 2px 0 rgba(0,0,0,.05);position:relative;margin-bottom:1.5rem;width:100%}.card .card{box-shadow:none}@media print{.card{box-shadow:none;border:0}}.card-body{flex:1 1 auto;margin:0;padding:1.5rem;position:relative}.card-body+.card-body{border-top:1px solid rgba(0,40,100,.12)}.card-body>:last-child{margin-bottom:0}@media print{.card-body{padding:0}}.card-body-scrollable{overflow:auto}.card-bottom,.card-footer{padding:1rem 1.5rem;background:0}.card-footer{border-top:1px solid rgba(0,40,100,.12);color:#6e7687}.card-header{background:0;padding:.5rem 1.5rem;display:flex;min-height:3.5rem;align-items:center}.card-header .card-title{margin-bottom:0}.card-header.border-0+.card-body{padding-top:0}@media print{.card-header{display:none}}.card-img-top{border-top-left-radius:3px;border-top-right-radius:3px}.card-img-overlay{background-color:rgba(0,0,0,.4);display:flex;flex-direction:column}.card-title{font-size:1.125rem;line-height:1.2;font-weight:400;margin-bottom:1.5rem}.card-title a{color:inherit}.card-title:only-child{margin-bottom:0}.card-subtitle,.card-title small{color:#9aa0ac;font-size:.875rem;display:block;margin:-.75rem 0 1rem;line-height:1.1;font-weight:400}.card-table{margin-bottom:0}.card-table tr:first-child td,.card-table tr:first-child th{border-top:0}.card-table tr td:first-child,.card-table tr th:first-child{padding-left:1.5rem}.card-table tr td:last-child,.card-table tr th:last-child{padding-right:1.5rem}.card-body+.card-table{border-top:1px solid rgba(0,40,100,.12)}.card-profile .card-header{height:9rem;background-size:cover}.card-profile-img{max-width:6rem;margin-top:-5rem;margin-bottom:1rem;border:3px solid #fff;border-radius:100%;box-shadow:0 1px 1px rgba(0,0,0,.1)}.card-link+.card-link{margin-left:1rem}.card-body+.card-list-group{border-top:1px solid rgba(0,40,100,.12)}.card-list-group .list-group-item{border-right:0;border-left:0;border-radius:0;padding-left:1.5rem;padding-right:1.5rem}.card-list-group .list-group-item:last-child{border-bottom:0}.card-list-group .list-group-item:first-child{border-top:0}.card-header-tabs{margin:-1.25rem 0;border-bottom:0;line-height:2rem}.card-header-tabs .nav-item{margin-bottom:1px}.card-header-pills{margin:-.75rem 0}.card-aside{flex-direction:row}.card-aside-column{min-width:5rem;width:30%;flex:0 0 30%;border-top-left-radius:3px;border-bottom-left-radius:3px;background:no-repeat 50%/cover}.card-value{font-size:2.5rem;line-height:3.4rem;height:3.4rem;display:flex;align-items:center;font-weight:400}.card-value i{vertical-align:middle}.card-chart-bg{height:4rem;margin-top:-1rem;position:relative;z-index:1;overflow:hidden}.card-options{margin-left:auto;display:flex;order:100;margin-right:-.5rem;color:#9aa0ac;align-self:center}.card-options a{margin-left:.5rem;color:#9aa0ac;display:inline-block;min-width:1rem}.card-options a:hover{text-decoration:none;color:#6e7687}.card-options a i{font-size:1rem;vertical-align:middle}.card-collapsed>:not(.card-header):not(.card-status),.card-options .dropdown-toggle:after{display:none}.card-collapsed .card-options-collapse i:before{content:"\E92D"}.card-fullscreen .card-options-fullscreen i:before{content:"\E992"}.card-fullscreen .card-options-remove{display:none}.card-map{height:15rem;background:#e9ecef}.card-map-placeholder{background:no-repeat 50%}.card-tabs{display:flex}.card-tabs-bottom .card-tabs-item{border:0;border-top:1px solid rgba(0,40,100,.12)}.card-tabs-bottom .card-tabs-item.active{border-top-color:#fff}.card-tabs-item{flex:1 1 auto;display:block;padding:1rem 1.5rem;border-bottom:1px solid rgba(0,40,100,.12);color:inherit;overflow:hidden}a.card-tabs-item{background:#fafbfc}a.card-tabs-item:hover{text-decoration:none;color:inherit}a.card-tabs-item:focus{z-index:1}a.card-tabs-item.active{background:#fff;border-bottom-color:#fff}.card-tabs-item+.card-tabs-item{border-left:1px solid rgba(0,40,100,.12)}.card-status{position:absolute;top:-1px;left:-1px;right:-1px;height:3px;border-radius:3px 3px 0 0;background:rgba(0,40,100,.12)}.card-status-left{right:auto;bottom:0;height:auto;width:3px;border-radius:3px 0 0 3px}.card-icon{width:3rem;font-size:2.5rem;line-height:3rem;text-align:center}.card-fullscreen{position:fixed;top:0;left:0;right:0;bottom:0;z-index:1;margin:0}.card-alert{border-radius:0;margin:-1px -1px 0}.card-category{font-size:.875rem;text-transform:uppercase;text-align:center;font-weight:600;letter-spacing:.05em;margin:0 0 .5rem}.popover{-webkit-filter:drop-shadow(0 1px 3px rgba(0,0,0,.1));filter:drop-shadow(0 1px 3px rgba(0,0,0,.1))}.popover.bs-popover-auto[x-placement^=top],.popover.bs-popover-top{margin-bottom:.625rem}.popover .arrow{margin-left:calc(.25rem + 2px)}.dropdown{display:inline-block}.dropdown-menu{box-shadow:0 1px 2px 0 rgba(0,0,0,.05);min-width:12rem}.dropdown-item{color:#6e7687}.dropdown-menu-arrow:before{top:-6px;border-bottom:5px solid rgba(0,0,0,.2)}.dropdown-menu-arrow:after,.dropdown-menu-arrow:before{position:absolute;left:12px;display:inline-block;border-right:5px solid transparent;border-left:5px solid transparent;content:""}.dropdown-menu-arrow:after{top:-5px;border-bottom:5px solid #fff}.dropdown-menu-arrow.dropdown-menu-right:after,.dropdown-menu-arrow.dropdown-menu-right:before{left:auto;right:12px}.dropdown-toggle{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:pointer}.dropdown-toggle:after{vertical-align:.155em}.dropdown-toggle:empty:after{margin-left:0}.dropdown-icon{color:#9aa0ac;margin-right:.5rem;margin-left:-.5rem;width:1em;display:inline-block;text-align:center;vertical-align:-1px}.list-inline-dots .list-inline-item+.list-inline-item:before{content:"\B7 ";margin-left:-2px;margin-right:3px}.list-separated-item{padding:1rem 0}.list-separated-item:first-child{padding-top:0}.list-separated-item:last-child{padding-bottom:0}.list-separated-item+.list-separated-item{border-top:1px solid rgba(0,40,100,.12)}.list-group-item.active .icon{color:inherit !important}.list-group-transparent .list-group-item{background:0;border:0;padding:.5rem 1rem;border-radius:3px}.list-group-transparent .list-group-item.active{background:rgba(70,127,207,.06);font-weight:600}.avatar{width:2rem;height:2rem;line-height:2rem;border-radius:50%;display:inline-block;background:#ced4da no-repeat 50%/cover;position:relative;text-align:center;color:#868e96;font-weight:600;vertical-align:bottom;font-size:.875rem;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.avatar i{font-size:125%;vertical-align:sub}.avatar-status{position:absolute;right:-2px;bottom:-2px;width:.75rem;height:.75rem;border:2px solid #fff;background:#868e96;border-radius:50%}.avatar-sm{width:1.5rem;height:1.5rem;line-height:1.5rem;font-size:.75rem}.avatar-md{width:2.5rem;height:2.5rem;line-height:2.5rem;font-size:1rem}.avatar-lg{width:3rem;height:3rem;line-height:3rem;font-size:1.25rem}.avatar-xl{width:4rem;height:4rem;line-height:4rem;font-size:1.75rem}.avatar-xxl{width:5rem;height:5rem;line-height:5rem;font-size:2rem}.avatar-placeholder{background:#ced4da url('data:image/svg+xml;charset=utf8,') no-repeat 50%/80%}.avatar-list{margin:0 0 -.5rem;padding:0;font-size:0}.avatar-list .avatar{margin-bottom:.5rem}.avatar-list .avatar:not(:last-child){margin-right:.5rem}.avatar-list-stacked .avatar{margin-right:-.8em !important;box-shadow:0 0 0 2px #fff}.avatar-blue{background-color:#c8d9f1;color:#467fcf}.avatar-indigo{background-color:#d1d5f0;color:#6574cd}.avatar-purple{background-color:#e4cff9;color:#a55eea}.avatar-pink{background-color:#fcd3e1;color:#f66d9b}.avatar-red{background-color:#f0bcbc;color:#cd201f}.avatar-orange{background-color:#fee0c7;color:#fd9644}.avatar-yellow{background-color:#fbedb7;color:#f1c40f}.avatar-green{background-color:#cfeab3;color:#5eba00}.avatar-teal{background-color:#bfefea;color:#2bcbba}.avatar-cyan{background-color:#b9e3ea;color:#17a2b8}.avatar-white{background-color:#fff;color:#fff}.avatar-gray{background-color:#dbdde0;color:#868e96}.avatar-gray-dark{background-color:#c2c4c6;color:#343a40}.avatar-azure{background-color:#c7e6fb;color:#45aaf2}.avatar-lime{background-color:#d7f2c2;color:#7bd235}.product-price{font-size:1rem}.product-price strong{font-size:1.5rem}@-webkit-keyframes indeterminate{0%{left:-35%;right:100%}60%,to{left:100%;right:-90%}}@keyframes indeterminate{0%{left:-35%;right:100%}60%,to{left:100%;right:-90%}}@-webkit-keyframes indeterminate-short{0%{left:-200%;right:100%}60%,to{left:107%;right:-8%}}@keyframes indeterminate-short{0%{left:-200%;right:100%}60%,to{left:107%;right:-8%}}.progress{position:relative}.progress-xs,.progress-xs .progress-bar{height:.25rem}.progress-sm,.progress-sm .progress-bar{height:.5rem}.progress-bar-indeterminate:after,.progress-bar-indeterminate:before{content:"";position:absolute;background-color:inherit;left:0;will-change:left,right;top:0;bottom:0}.progress-bar-indeterminate:before{-webkit-animation:indeterminate 2.1s cubic-bezier(.65,.815,.735,.395) infinite;animation:indeterminate 2.1s cubic-bezier(.65,.815,.735,.395) infinite}.progress-bar-indeterminate:after{-webkit-animation:indeterminate-short 2.1s cubic-bezier(.165,.84,.44,1) infinite;animation:indeterminate-short 2.1s cubic-bezier(.165,.84,.44,1) infinite;-webkit-animation-delay:1.15s;animation-delay:1.15s}@-webkit-keyframes loader{0%{-webkit-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes loader{0%{-webkit-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.dimmer{position:relative}.dimmer .loader{display:none;margin:0 auto;position:absolute;top:50%;left:0;right:0;-webkit-transform:translateY(-50%);transform:translateY(-50%)}.dimmer.active .loader{display:block}.dimmer.active .dimmer-content{opacity:.04;pointer-events:none}.loader{display:block;position:relative;height:2.5rem;width:2.5rem;color:#467fcf}.loader:after,.loader:before{width:2.5rem;height:2.5rem;margin:-1.25rem 0 0 -1.25rem;position:absolute;content:"";top:50%;left:50%}.loader:before{border-radius:50%;border:3px solid;opacity:.15}.loader:after{-webkit-animation:loader .6s linear;animation:loader .6s linear;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite;border-radius:50%;border:3px solid transparent;border-top-color:currentcolor;box-shadow:0 0 0 1px transparent}.icons-list{list-style:none;margin:0 -1px -1px 0;padding:0;display:flex;flex-wrap:wrap}.icons-list>li{flex:1 0 4rem}.icons-list-wrap{overflow:hidden}.icons-list-item{text-align:center;height:4rem;display:flex;align-items:center;justify-content:center;border-right:1px solid rgba(0,40,100,.12);border-bottom:1px solid rgba(0,40,100,.12)}.icons-list-item i{font-size:1.25rem}.img-gallery{margin-right:-.25rem;margin-left:-.25rem;margin-bottom:-.5rem}.img-gallery>.col,.img-gallery>[class*=col-]{padding-left:.25rem;padding-right:.25rem;padding-bottom:.5rem}.link-overlay{position:relative}.link-overlay:hover .link-overlay-bg{opacity:1}.link-overlay-bg{position:absolute;top:0;left:0;right:0;bottom:0;background:rgba(70,127,207,.8);display:flex;color:#fff;align-items:center;justify-content:center;font-size:1.25rem;opacity:0;transition:opacity .3s}.media-icon{width:2rem;height:2rem;line-height:2rem;text-align:center;border-radius:100%}.media-list{margin:0;padding:0;list-style:none}textarea[cols]{height:auto}.form-group,.form-label{display:block}.form-label{margin-bottom:.375rem;font-weight:600;font-size:.875rem}.form-label-small{float:right;font-weight:400;font-size:87.5%}.form-footer{margin-top:2rem}.custom-control{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.custom-controls-stacked .custom-control{margin-bottom:.25rem}.custom-control-label{vertical-align:middle}.custom-control-label:before{border:1px solid rgba(0,40,100,.12);background-color:#fff;background-size:.5rem}.custom-control-description{line-height:1.5rem}.input-group-append,.input-group-btn,.input-group-prepend{font-size:.9375rem}.input-group-append>.btn,.input-group-btn>.btn,.input-group-prepend>.btn{height:100%;border-color:rgba(0,40,100,.12)}.input-group-prepend>.input-group-text{border-right:0}.input-group-append>.input-group-text{border-left:0}.input-icon{position:relative}.input-icon .form-control:not(:last-child){padding-right:2.5rem}.input-icon .form-control:not(:first-child){padding-left:2.5rem}.input-icon-addon{position:absolute;top:0;bottom:0;left:0;color:#9aa0ac;display:flex;align-items:center;justify-content:center;min-width:2.5rem;pointer-events:none}.input-icon-addon:last-child{left:auto;right:0}.form-fieldset{background:#f8f9fa;border:1px solid #e9ecef;padding:1rem;border-radius:3px;margin-bottom:1rem}.form-required{color:#cd201f}.form-required:before{content:" "}.state-valid{padding-right:2rem;background:url("data:image/svg+xml;charset=utf8,") no-repeat center right .5rem/1rem}.state-invalid{padding-right:2rem;background:url("data:image/svg+xml;charset=utf8,") no-repeat center right .5rem/1rem}.form-help{display:inline-block;width:1rem;height:1rem;text-align:center;line-height:1rem;color:#9aa0ac;background:#f8f9fa;border-radius:50%;font-size:.75rem;transition:background-color .3s,color .3s;text-decoration:none;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.form-help:hover,.form-help[aria-describedby]{background:#467fcf;color:#fff}.sparkline{display:inline-block;height:2rem}.jqstooltip{box-sizing:initial;font-family:inherit !important;background:#333 !important;border:none !important;border-radius:3px;font-size:11px !important;font-weight:700 !important;line-height:1 !important;padding:6px !important}.jqstooltip .jqsfield{font:inherit !important}.social-links li a{background:#f8f8f8;border-radius:50%;color:#9aa0ac;display:inline-block;height:1.75rem;width:1.75rem;line-height:1.75rem;text-align:center}.chart,.map{position:relative;padding-top:56.25%}.chart-square,.map-square{padding-top:100%}.chart-content,.map-content{position:absolute;top:0;left:0;right:0;bottom:0}.map-header{margin-top:-1.5rem;height:15rem;position:relative;margin-bottom:-1.5rem}.map-header:before{content:"";position:absolute;bottom:0;left:0;right:0;height:10rem;background:linear-gradient(180deg,rgba(245,247,251,0) 5%,#f5f7fb 95%);pointer-events:none}.map-header-layer{height:100%}.map-static{height:120px;width:100%;max-width:640px;background-position:50%;background-size:640px 120px}@-webkit-keyframes status-pulse{0%,to{opacity:1}50%{opacity:.32}}@keyframes status-pulse{0%,to{opacity:1}50%{opacity:.32}}.status-icon{content:"";width:.5rem;height:.5rem;display:inline-block;background:currentColor;border-radius:50%;-webkit-transform:translateY(-1px);transform:translateY(-1px);margin-right:.375rem;vertical-align:middle}.status-animated{-webkit-animation:status-pulse 1s ease infinite;animation:status-pulse 1s ease infinite}.chart-circle{display:block;height:8rem;width:8rem;position:relative}.chart-circle canvas{margin:0 auto;display:block;max-width:100%;max-height:100%}.chart-circle-xs{height:2.5rem;width:2.5rem;font-size:.8rem}.chart-circle-sm{height:4rem;width:4rem;font-size:.8rem}.chart-circle-lg{height:10rem;width:10rem;font-size:.8rem}.chart-circle-value{position:absolute;top:0;left:0;right:0;margin-left:auto;margin-right:auto;bottom:0;display:flex;justify-content:center;align-items:center;flex-direction:column;line-height:1}.chart-circle-value small{display:block;color:#9aa0ac;font-size:.9375rem}.chips{margin:0 0 -.5rem}.chips .chip{margin:0 .5rem .5rem 0}.chip{display:inline-block;height:2rem;line-height:2rem;font-size:.875rem;font-weight:500;color:#6e7687;padding:0 .75rem;border-radius:1rem;background-color:#f8f9fa;transition:background .3s}.chip .avatar{float:left;margin:0 .5rem 0 -.75rem;height:2rem;width:2rem;border-radius:50%}a.chip:hover{color:inherit;text-decoration:none;background-color:#e9ecef}.stamp{color:#fff;background:#868e96;display:inline-block;min-width:2rem;height:2rem;padding:0 .25rem;line-height:2rem;text-align:center;border-radius:3px;font-weight:600}.stamp-md{min-width:2.5rem;height:2.5rem;line-height:2.5rem}.chat{outline:0;margin:0;list-style-type:none;flex-direction:column;justify-content:flex-end;min-height:100%}.chat,.chat-line{padding:0;display:flex}.chat-line{text-align:right;position:relative;flex-direction:row-reverse}.chat-line+.chat-line{padding-top:1rem}.chat-message{position:relative;display:inline-block;background-color:#467fcf;color:#fff;font-size:.875rem;padding:.375rem .5rem;border-radius:3px;white-space:normal;text-align:left;margin:0 .5rem 0 2.5rem;line-height:1.4}.chat-message>:last-child{margin-bottom:0 !important}.chat-message:after{content:"";position:absolute;right:-5px;top:7px;border-bottom:6px solid transparent;border-left:6px solid #467fcf;border-top:6px solid transparent}.chat-message img{max-width:100%}.chat-message p{margin-bottom:1em}.chat-line-friend{flex-direction:row}.chat-line-friend+.chat-line-friend{margin-top:-.5rem}.chat-line-friend+.chat-line-friend .chat-author{visibility:hidden}.chat-line-friend+.chat-line-friend .chat-message:after{display:none}.chat-line-friend .chat-message{background-color:#f3f3f3;color:#495057;margin-left:.5rem;margin-right:2.5rem}.chat-line-friend .chat-message:after{right:auto;left:-5px;border-left-width:0;border-right:5px solid #f3f3f3}.example{padding:1.5rem;border:1px solid rgba(0,40,100,.12);border-radius:3px 3px 0 0;font-size:.9375rem}.example-bg{background:#f5f7fb}.example+.highlight{border-top:0;margin-top:0;border-radius:0 0 3px 3px}.highlight{margin:1rem 0 2rem;border:1px solid rgba(0,40,100,.12);border-radius:3px;font-size:.9375rem;max-height:40rem;overflow:auto;background:#fcfcfc}.highlight pre{margin-bottom:0;background-color:initial}.example-column{margin:0 auto}.example-column>.card:last-of-type{margin-bottom:0}.example-column-1{max-width:20rem}.example-column-2{max-width:40rem}.tag{font-size:.75rem;color:#6e7687;background-color:#e9ecef;border-radius:3px;padding:0 .5rem;line-height:2em;display:inline-flex;cursor:default;font-weight:400;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}a.tag{text-decoration:none;cursor:pointer;transition:color .3s,background .3s}a.tag:hover{background-color:rgba(110,118,135,.2);color:inherit}.tag-addon{display:inline-block;padding:0 .5rem;color:inherit;text-decoration:none;background:rgba(0,0,0,.06);margin:0 -.5rem 0 .5rem;text-align:center;min-width:1.5rem}.tag-addon:last-child{border-top-right-radius:3px;border-bottom-right-radius:3px}.tag-addon i{vertical-align:middle;margin:0 -.25rem}a.tag-addon{text-decoration:none;cursor:pointer;transition:color .3s,background .3s}a.tag-addon:hover{background:rgba(0,0,0,.16);color:inherit}.tag-avatar{width:1.5rem;height:1.5rem;border-radius:3px 0 0 3px;margin:0 .5rem 0 -.5rem}.tag-blue{background-color:#467fcf;color:#fff}.tag-indigo{background-color:#6574cd;color:#fff}.tag-purple{background-color:#a55eea;color:#fff}.tag-pink{background-color:#f66d9b;color:#fff}.tag-red{background-color:#cd201f;color:#fff}.tag-orange{background-color:#fd9644;color:#fff}.tag-yellow{background-color:#f1c40f;color:#fff}.tag-green{background-color:#5eba00;color:#fff}.tag-teal{background-color:#2bcbba;color:#fff}.tag-cyan{background-color:#17a2b8;color:#fff}.tag-white{background-color:#fff;color:#fff}.tag-gray{background-color:#868e96;color:#fff}.tag-gray-dark{background-color:#343a40;color:#fff}.tag-azure{background-color:#45aaf2;color:#fff}.tag-lime{background-color:#7bd235;color:#fff}.tag-primary{background-color:#467fcf;color:#fff}.tag-secondary{background-color:#868e96;color:#fff}.tag-success{background-color:#5eba00;color:#fff}.tag-info{background-color:#45aaf2;color:#fff}.tag-warning{background-color:#f1c40f;color:#fff}.tag-danger{background-color:#cd201f;color:#fff}.tag-light{background-color:#f8f9fa;color:#fff}.tag-dark{background-color:#343a40;color:#fff}.tag-rounded,.tag-rounded .tag-avatar{border-radius:50px}.tags{margin-bottom:-.5rem;font-size:0}.tags>.tag{margin-bottom:.5rem}.tags>.tag:not(:last-child){margin-right:.5rem}.highlight .hll{background-color:#ffc}.highlight .c{color:#999}.highlight .k{color:#069}.highlight .o{color:#555}.highlight .cm{color:#999}.highlight .cp{color:#099}.highlight .c1,.highlight .cs{color:#999}.highlight .gd{background-color:#fcc;border:1px solid #c00}.highlight .ge{font-style:italic}.highlight .gr{color:red}.highlight .gh{color:#030}.highlight .gi{background-color:#cfc;border:1px solid #0c0}.highlight .go{color:#aaa}.highlight .gp{color:#009}.highlight .gu{color:#030}.highlight .gt{color:#9c6}.highlight .kc,.highlight .kd,.highlight .kn,.highlight .kp,.highlight .kr{color:#069}.highlight .kt{color:#078}.highlight .m{color:#f60}.highlight .s{color:#d44950}.highlight .na{color:#4f9fcf}.highlight .nb{color:#366}.highlight .nc{color:#0a8}.highlight .no{color:#360}.highlight .nd{color:#99f}.highlight .ni{color:#999}.highlight .ne{color:#c00}.highlight .nf{color:#c0f}.highlight .nl{color:#99f}.highlight .nn{color:#0cf}.highlight .nt{color:#2f6f9f}.highlight .nv{color:#033}.highlight .ow{color:#000}.highlight .w{color:#bbb}.highlight .mf,.highlight .mh,.highlight .mi,.highlight .mo{color:#f60}.highlight .sb,.highlight .sc{color:#c30}.highlight .sd{font-style:italic;color:#c30}.highlight .s2,.highlight .se,.highlight .sh{color:#c30}.highlight .si{color:#a00}.highlight .sx{color:#c30}.highlight .sr{color:#3aa}.highlight .s1{color:#c30}.highlight .ss{color:#fc3}.highlight .bp{color:#366}.highlight .vc,.highlight .vg,.highlight .vi{color:#033}.highlight .il{color:#f60}.highlight .css .nt+.nt,.highlight .css .o,.highlight .css .o+.nt{color:#999}.highlight .language-bash:before,.highlight .language-sh:before{color:#009;content:"$ ";-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.highlight .language-powershell:before{color:#009;content:"PM> ";-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.custom-range{align-items:center;-webkit-appearance:none;-moz-appearance:none;appearance:none;background:0;cursor:pointer;display:flex;height:100%;min-height:2.375rem;overflow:hidden;padding:0;border:0}.custom-range:focus{box-shadow:none;outline:0}.custom-range:focus::-webkit-slider-thumb{border-color:#467fcf;background-color:#467fcf}.custom-range:focus::-moz-range-thumb{border-color:#467fcf;background-color:#467fcf}.custom-range:focus::-ms-thumb{border-color:#467fcf;background-color:#467fcf}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-runnable-track{background:#467fcf;content:"";height:2px;pointer-events:none}.custom-range::-webkit-slider-thumb{width:14px;height:14px;-webkit-appearance:none;appearance:none;background:#fff;border-radius:50px;box-shadow:1px 0 0 -6px rgba(0,50,126,.12),6px 0 0 -6px rgba(0,50,126,.12),7px 0 0 -6px rgba(0,50,126,.12),8px 0 0 -6px rgba(0,50,126,.12),9px 0 0 -6px rgba(0,50,126,.12),10px 0 0 -6px rgba(0,50,126,.12),11px 0 0 -6px rgba(0,50,126,.12),12px 0 0 -6px rgba(0,50,126,.12),13px 0 0 -6px rgba(0,50,126,.12),14px 0 0 -6px rgba(0,50,126,.12),15px 0 0 -6px rgba(0,50,126,.12),16px 0 0 -6px rgba(0,50,126,.12),17px 0 0 -6px rgba(0,50,126,.12),18px 0 0 -6px rgba(0,50,126,.12),19px 0 0 -6px rgba(0,50,126,.12),20px 0 0 -6px rgba(0,50,126,.12),21px 0 0 -6px rgba(0,50,126,.12),22px 0 0 -6px rgba(0,50,126,.12),23px 0 0 -6px rgba(0,50,126,.12),24px 0 0 -6px rgba(0,50,126,.12),25px 0 0 -6px rgba(0,50,126,.12),26px 0 0 -6px rgba(0,50,126,.12),27px 0 0 -6px rgba(0,50,126,.12),28px 0 0 -6px rgba(0,50,126,.12),29px 0 0 -6px rgba(0,50,126,.12),30px 0 0 -6px rgba(0,50,126,.12),31px 0 0 -6px rgba(0,50,126,.12),32px 0 0 -6px rgba(0,50,126,.12),33px 0 0 -6px rgba(0,50,126,.12),34px 0 0 -6px rgba(0,50,126,.12),35px 0 0 -6px rgba(0,50,126,.12),36px 0 0 -6px rgba(0,50,126,.12),37px 0 0 -6px rgba(0,50,126,.12),38px 0 0 -6px rgba(0,50,126,.12),39px 0 0 -6px rgba(0,50,126,.12),40px 0 0 -6px rgba(0,50,126,.12),41px 0 0 -6px rgba(0,50,126,.12),42px 0 0 -6px rgba(0,50,126,.12),43px 0 0 -6px rgba(0,50,126,.12),44px 0 0 -6px rgba(0,50,126,.12),45px 0 0 -6px rgba(0,50,126,.12),46px 0 0 -6px rgba(0,50,126,.12),47px 0 0 -6px rgba(0,50,126,.12),48px 0 0 -6px rgba(0,50,126,.12),49px 0 0 -6px rgba(0,50,126,.12),50px 0 0 -6px rgba(0,50,126,.12),51px 0 0 -6px rgba(0,50,126,.12),52px 0 0 -6px rgba(0,50,126,.12),53px 0 0 -6px rgba(0,50,126,.12),54px 0 0 -6px rgba(0,50,126,.12),55px 0 0 -6px rgba(0,50,126,.12),56px 0 0 -6px rgba(0,50,126,.12),57px 0 0 -6px rgba(0,50,126,.12),58px 0 0 -6px rgba(0,50,126,.12),59px 0 0 -6px rgba(0,50,126,.12),60px 0 0 -6px rgba(0,50,126,.12),61px 0 0 -6px rgba(0,50,126,.12),62px 0 0 -6px rgba(0,50,126,.12),63px 0 0 -6px rgba(0,50,126,.12),64px 0 0 -6px rgba(0,50,126,.12),65px 0 0 -6px rgba(0,50,126,.12),66px 0 0 -6px rgba(0,50,126,.12),67px 0 0 -6px rgba(0,50,126,.12),68px 0 0 -6px rgba(0,50,126,.12),69px 0 0 -6px rgba(0,50,126,.12),70px 0 0 -6px rgba(0,50,126,.12),71px 0 0 -6px rgba(0,50,126,.12),72px 0 0 -6px rgba(0,50,126,.12),73px 0 0 -6px rgba(0,50,126,.12),74px 0 0 -6px rgba(0,50,126,.12),75px 0 0 -6px rgba(0,50,126,.12),76px 0 0 -6px rgba(0,50,126,.12),77px 0 0 -6px rgba(0,50,126,.12),78px 0 0 -6px rgba(0,50,126,.12),79px 0 0 -6px rgba(0,50,126,.12),80px 0 0 -6px rgba(0,50,126,.12),81px 0 0 -6px rgba(0,50,126,.12),82px 0 0 -6px rgba(0,50,126,.12),83px 0 0 -6px rgba(0,50,126,.12),84px 0 0 -6px rgba(0,50,126,.12),85px 0 0 -6px rgba(0,50,126,.12),86px 0 0 -6px rgba(0,50,126,.12),87px 0 0 -6px rgba(0,50,126,.12),88px 0 0 -6px rgba(0,50,126,.12),89px 0 0 -6px rgba(0,50,126,.12),90px 0 0 -6px rgba(0,50,126,.12),91px 0 0 -6px rgba(0,50,126,.12),92px 0 0 -6px rgba(0,50,126,.12),93px 0 0 -6px rgba(0,50,126,.12),94px 0 0 -6px rgba(0,50,126,.12),95px 0 0 -6px rgba(0,50,126,.12),96px 0 0 -6px rgba(0,50,126,.12),97px 0 0 -6px rgba(0,50,126,.12),98px 0 0 -6px rgba(0,50,126,.12),99px 0 0 -6px rgba(0,50,126,.12),100px 0 0 -6px rgba(0,50,126,.12),101px 0 0 -6px rgba(0,50,126,.12),102px 0 0 -6px rgba(0,50,126,.12),103px 0 0 -6px rgba(0,50,126,.12),104px 0 0 -6px rgba(0,50,126,.12),105px 0 0 -6px rgba(0,50,126,.12),106px 0 0 -6px rgba(0,50,126,.12),107px 0 0 -6px rgba(0,50,126,.12),108px 0 0 -6px rgba(0,50,126,.12),109px 0 0 -6px rgba(0,50,126,.12),110px 0 0 -6px rgba(0,50,126,.12),111px 0 0 -6px rgba(0,50,126,.12),112px 0 0 -6px rgba(0,50,126,.12),113px 0 0 -6px rgba(0,50,126,.12),114px 0 0 -6px rgba(0,50,126,.12),115px 0 0 -6px rgba(0,50,126,.12),116px 0 0 -6px rgba(0,50,126,.12),117px 0 0 -6px rgba(0,50,126,.12),118px 0 0 -6px rgba(0,50,126,.12),119px 0 0 -6px rgba(0,50,126,.12),120px 0 0 -6px rgba(0,50,126,.12),121px 0 0 -6px rgba(0,50,126,.12),122px 0 0 -6px rgba(0,50,126,.12),123px 0 0 -6px rgba(0,50,126,.12),124px 0 0 -6px rgba(0,50,126,.12),125px 0 0 -6px rgba(0,50,126,.12),126px 0 0 -6px rgba(0,50,126,.12),127px 0 0 -6px rgba(0,50,126,.12),128px 0 0 -6px rgba(0,50,126,.12),129px 0 0 -6px rgba(0,50,126,.12),130px 0 0 -6px rgba(0,50,126,.12),131px 0 0 -6px rgba(0,50,126,.12),132px 0 0 -6px rgba(0,50,126,.12),133px 0 0 -6px rgba(0,50,126,.12),134px 0 0 -6px rgba(0,50,126,.12),135px 0 0 -6px rgba(0,50,126,.12),136px 0 0 -6px rgba(0,50,126,.12),137px 0 0 -6px rgba(0,50,126,.12),138px 0 0 -6px rgba(0,50,126,.12),139px 0 0 -6px rgba(0,50,126,.12),140px 0 0 -6px rgba(0,50,126,.12),141px 0 0 -6px rgba(0,50,126,.12),142px 0 0 -6px rgba(0,50,126,.12),143px 0 0 -6px rgba(0,50,126,.12),144px 0 0 -6px rgba(0,50,126,.12),145px 0 0 -6px rgba(0,50,126,.12),146px 0 0 -6px rgba(0,50,126,.12),147px 0 0 -6px rgba(0,50,126,.12),148px 0 0 -6px rgba(0,50,126,.12),149px 0 0 -6px rgba(0,50,126,.12),150px 0 0 -6px rgba(0,50,126,.12),151px 0 0 -6px rgba(0,50,126,.12),152px 0 0 -6px rgba(0,50,126,.12),153px 0 0 -6px rgba(0,50,126,.12),154px 0 0 -6px rgba(0,50,126,.12),155px 0 0 -6px rgba(0,50,126,.12),156px 0 0 -6px rgba(0,50,126,.12),157px 0 0 -6px rgba(0,50,126,.12),158px 0 0 -6px rgba(0,50,126,.12),159px 0 0 -6px rgba(0,50,126,.12),160px 0 0 -6px rgba(0,50,126,.12),161px 0 0 -6px rgba(0,50,126,.12),162px 0 0 -6px rgba(0,50,126,.12),163px 0 0 -6px rgba(0,50,126,.12),164px 0 0 -6px rgba(0,50,126,.12),165px 0 0 -6px rgba(0,50,126,.12),166px 0 0 -6px rgba(0,50,126,.12),167px 0 0 -6px rgba(0,50,126,.12),168px 0 0 -6px rgba(0,50,126,.12),169px 0 0 -6px rgba(0,50,126,.12),170px 0 0 -6px rgba(0,50,126,.12),171px 0 0 -6px rgba(0,50,126,.12),172px 0 0 -6px rgba(0,50,126,.12),173px 0 0 -6px rgba(0,50,126,.12),174px 0 0 -6px rgba(0,50,126,.12),175px 0 0 -6px rgba(0,50,126,.12),176px 0 0 -6px rgba(0,50,126,.12),177px 0 0 -6px rgba(0,50,126,.12),178px 0 0 -6px rgba(0,50,126,.12),179px 0 0 -6px rgba(0,50,126,.12),180px 0 0 -6px rgba(0,50,126,.12),181px 0 0 -6px rgba(0,50,126,.12),182px 0 0 -6px rgba(0,50,126,.12),183px 0 0 -6px rgba(0,50,126,.12),184px 0 0 -6px rgba(0,50,126,.12),185px 0 0 -6px rgba(0,50,126,.12),186px 0 0 -6px rgba(0,50,126,.12),187px 0 0 -6px rgba(0,50,126,.12),188px 0 0 -6px rgba(0,50,126,.12),189px 0 0 -6px rgba(0,50,126,.12),190px 0 0 -6px rgba(0,50,126,.12),191px 0 0 -6px rgba(0,50,126,.12),192px 0 0 -6px rgba(0,50,126,.12),193px 0 0 -6px rgba(0,50,126,.12),194px 0 0 -6px rgba(0,50,126,.12),195px 0 0 -6px rgba(0,50,126,.12),196px 0 0 -6px rgba(0,50,126,.12),197px 0 0 -6px rgba(0,50,126,.12),198px 0 0 -6px rgba(0,50,126,.12),199px 0 0 -6px rgba(0,50,126,.12),200px 0 0 -6px rgba(0,50,126,.12),201px 0 0 -6px rgba(0,50,126,.12),202px 0 0 -6px rgba(0,50,126,.12),203px 0 0 -6px rgba(0,50,126,.12),204px 0 0 -6px rgba(0,50,126,.12),205px 0 0 -6px rgba(0,50,126,.12),206px 0 0 -6px rgba(0,50,126,.12),207px 0 0 -6px rgba(0,50,126,.12),208px 0 0 -6px rgba(0,50,126,.12),209px 0 0 -6px rgba(0,50,126,.12),210px 0 0 -6px rgba(0,50,126,.12),211px 0 0 -6px rgba(0,50,126,.12),212px 0 0 -6px rgba(0,50,126,.12),213px 0 0 -6px rgba(0,50,126,.12),214px 0 0 -6px rgba(0,50,126,.12),215px 0 0 -6px rgba(0,50,126,.12),216px 0 0 -6px rgba(0,50,126,.12),217px 0 0 -6px rgba(0,50,126,.12),218px 0 0 -6px rgba(0,50,126,.12),219px 0 0 -6px rgba(0,50,126,.12),220px 0 0 -6px rgba(0,50,126,.12),221px 0 0 -6px rgba(0,50,126,.12),222px 0 0 -6px rgba(0,50,126,.12),223px 0 0 -6px rgba(0,50,126,.12),224px 0 0 -6px rgba(0,50,126,.12),225px 0 0 -6px rgba(0,50,126,.12),226px 0 0 -6px rgba(0,50,126,.12),227px 0 0 -6px rgba(0,50,126,.12),228px 0 0 -6px rgba(0,50,126,.12),229px 0 0 -6px rgba(0,50,126,.12),230px 0 0 -6px rgba(0,50,126,.12),231px 0 0 -6px rgba(0,50,126,.12),232px 0 0 -6px rgba(0,50,126,.12),233px 0 0 -6px rgba(0,50,126,.12),234px 0 0 -6px rgba(0,50,126,.12),235px 0 0 -6px rgba(0,50,126,.12),236px 0 0 -6px rgba(0,50,126,.12),237px 0 0 -6px rgba(0,50,126,.12),238px 0 0 -6px rgba(0,50,126,.12),239px 0 0 -6px rgba(0,50,126,.12),240px 0 0 -6px rgba(0,50,126,.12);margin-top:-6px;border:1px solid rgba(0,30,75,.12);transition:border-color .3s,background-color .3s}.custom-range::-moz-range-track{width:240px;height:2px;background:rgba(0,50,126,.12)}.custom-range::-moz-range-thumb{width:14px;height:14px;background:#fff;border-radius:50px;border:1px solid rgba(0,30,75,.12);position:relative;transition:border-color .3s,background-color .3s}.custom-range::-moz-range-progress{height:2px;background:#467fcf;border:0;margin-top:0}.custom-range::-ms-track{background:transparent;border:0;border-color:transparent;border-radius:0;border-width:0;color:transparent;height:2px;margin-top:10px;width:240px}.custom-range::-ms-thumb{width:240px;height:2px;background:#fff;border-radius:50px;border:1px solid rgba(0,30,75,.12);transition:border-color .3s,background-color .3s}.custom-range::-ms-fill-lower{background:#467fcf;border-radius:0}.custom-range::-ms-fill-upper{background:rgba(0,50,126,.12);border-radius:0}.custom-range::-ms-tooltip{display:none}.selectgroup{display:inline-flex}.selectgroup-item{flex-grow:1;position:relative}.selectgroup-item+.selectgroup-item{margin-left:-1px}.selectgroup-item:not(:first-child) .selectgroup-button{border-top-left-radius:0;border-bottom-left-radius:0}.selectgroup-item:not(:last-child) .selectgroup-button{border-top-right-radius:0;border-bottom-right-radius:0}.selectgroup-input{opacity:0;position:absolute;z-index:-1;top:0;left:0}.selectgroup-button{display:block;border:1px solid rgba(0,40,100,.12);text-align:center;padding:.375rem 1rem;position:relative;cursor:pointer;border-radius:3px;color:#9aa0ac;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;font-size:.9375rem;line-height:1.5;min-width:2.375rem}.selectgroup-button-icon{padding-left:.5rem;padding-right:.5rem;font-size:1.125rem;line-height:1.125rem}.selectgroup-input:checked+.selectgroup-button{border-color:#467fcf;z-index:1;color:#467fcf;background:#edf2fa}.selectgroup-input:focus+.selectgroup-button{border-color:#467fcf;z-index:2;color:#467fcf;box-shadow:0 0 0 2px rgba(70,127,207,.25)}.selectgroup-pills{flex-wrap:wrap;align-items:flex-start}.selectgroup-pills .selectgroup-item{margin-right:.5rem;flex-grow:0}.selectgroup-pills .selectgroup-button{border-radius:50px !important}.custom-switch{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:default;display:inline-flex;align-items:center;margin:0}.custom-switch-input{position:absolute;z-index:-1;opacity:0}.custom-switches-stacked{display:flex;flex-direction:column}.custom-switches-stacked .custom-switch{margin-bottom:.5rem}.custom-switch-indicator{display:inline-block;height:1.25rem;width:2.25rem;background:#e9ecef;border-radius:50px;position:relative;vertical-align:bottom;border:1px solid rgba(0,40,100,.12);transition:border-color .3s,background-color .3s}.custom-switch-indicator:before{content:"";position:absolute;height:calc(1.25rem - 4px);width:calc(1.25rem - 4px);top:1px;left:1px;background:#fff;border-radius:50%;transition:left .3s;box-shadow:0 1px 2px 0 rgba(0,0,0,.4)}.custom-switch-input:checked~.custom-switch-indicator{background:#467fcf}.custom-switch-input:checked~.custom-switch-indicator:before{left:calc(1rem + 1px)}.custom-switch-input:focus~.custom-switch-indicator{box-shadow:0 0 0 2px rgba(70,127,207,.25);border-color:#467fcf}.custom-switch-description{margin-left:.5rem;color:#6e7687;transition:color .3s}.custom-switch-input:checked~.custom-switch-description{color:#495057}.imagecheck{margin:0;position:relative;cursor:pointer}.imagecheck-input{position:absolute;z-index:-1;opacity:0}.imagecheck-figure{border:1px solid rgba(0,40,100,.12);border-radius:3px;margin:0;position:relative}.imagecheck-input:focus~.imagecheck-figure{border-color:#467fcf;box-shadow:0 0 0 2px rgba(70,127,207,.25)}.imagecheck-input:checked~.imagecheck-figure{border-color:rgba(0,40,100,.24)}.imagecheck-figure:before{content:"";position:absolute;top:.25rem;left:.25rem;display:block;width:1rem;height:1rem;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background:#467fcf url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E") no-repeat 50%/50% 50%;color:#fff;z-index:1;border-radius:3px;opacity:0;transition:opacity .3s}.imagecheck-input:checked~.imagecheck-figure:before{opacity:1}.imagecheck-image{max-width:100%;opacity:.64;transition:opacity .3s}.imagecheck-image:first-child{border-top-left-radius:2px;border-top-right-radius:2px}.imagecheck-image:last-child{border-bottom-left-radius:2px;border-bottom-right-radius:2px}.imagecheck-input:checked~.imagecheck-figure .imagecheck-image,.imagecheck-input:focus~.imagecheck-figure .imagecheck-image,.imagecheck:hover .imagecheck-image{opacity:1}.imagecheck-caption{text-align:center;padding:.25rem;color:#9aa0ac;font-size:.875rem;transition:color .3s}.imagecheck-input:checked~.imagecheck-figure .imagecheck-caption,.imagecheck-input:focus~.imagecheck-figure .imagecheck-caption,.imagecheck:hover .imagecheck-caption{color:#495057}.colorinput{margin:0;position:relative;cursor:pointer}.colorinput-input{position:absolute;z-index:-1;opacity:0}.colorinput-color{display:inline-block;width:1.75rem;height:1.75rem;border-radius:3px;border:1px solid rgba(0,40,100,.12);color:#fff;box-shadow:0 1px 2px 0 rgba(0,0,0,.05)}.colorinput-color:before{content:"";opacity:0;position:absolute;top:.25rem;left:.25rem;height:1.25rem;width:1.25rem;transition:opacity .3s;background:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E") no-repeat 50%/50% 50%}.colorinput-input:checked~.colorinput-color:before{opacity:1}.colorinput-input:focus~.colorinput-color{border-color:#467fcf;box-shadow:0 0 0 2px rgba(70,127,207,.25)}.timeline{position:relative;margin:0 0 2rem;padding:0;list-style:none}.timeline:before{background-color:#e9ecef;position:absolute;display:block;content:"";width:1px;height:100%;top:0;bottom:0;left:4px}.timeline-item{position:relative;display:flex;padding-left:2rem;margin:.5rem 0}.timeline-item:first-child:before,.timeline-item:last-child:before{content:"";position:absolute;background:#fff;width:1px;left:.25rem}.timeline-item:first-child{margin-top:0}.timeline-item:first-child:before{top:0;height:.5rem}.timeline-item:last-child{margin-bottom:0}.timeline-item:last-child:before{top:.5rem;bottom:0}.timeline-badge{position:absolute;display:block;width:.4375rem;height:.4375rem;left:1px;top:.5rem;border-radius:100%;border:1px solid #fff;background:#adb5bd}.timeline-time{white-space:nowrap;margin-left:auto;color:#9aa0ac;font-size:87.5%}.browser{width:1.25rem;height:1.25rem;display:inline-block;background:no-repeat 50%/100% 100%;vertical-align:bottom;font-style:normal}.browser-android-browser{background-image:url(/static/media/android-browser.e1d3686c.svg)}.browser-aol-explorer{background-image:url(/static/media/aol-explorer.f2a4363b.svg)}.browser-blackberry{background-image:url(/static/media/blackberry.ead509ae.svg)}.browser-camino{background-image:url(/static/media/camino.23c5c7fa.svg)}.browser-chrome{background-image:url(/static/media/chrome.2bbe801c.svg)}.browser-chromium{background-image:url(/static/media/chromium.870087fd.svg)}.browser-dolphin{background-image:url(/static/media/dolphin.f66d5a06.svg)}.browser-edge{background-image:url(/static/media/edge.abda4ac1.svg)}.browser-firefox{background-image:url(/static/media/firefox.e037fac5.svg)}.browser-ie{background-image:url(/static/media/ie.57c3e539.svg)}.browser-maxthon{background-image:url(/static/media/maxthon.df51f6f4.svg)}.browser-mozilla{background-image:url(/static/media/mozilla.91974b40.svg)}.browser-netscape{background-image:url(/static/media/netscape.f64e6793.svg)}.browser-opera{background-image:url(/static/media/opera.438992de.svg)}.browser-safari{background-image:url(/static/media/safari.ee79ab6a.svg)}.browser-sleipnir{background-image:url(/static/media/sleipnir.1751c6d6.svg)}.browser-uc-browser{background-image:url(/static/media/uc-browser.f600350d.svg)}.browser-vivaldi{background-image:url(/static/media/vivaldi.6b04dfda.svg)}.flag{width:1.6rem;height:1.2rem;display:inline-block;background:no-repeat 50%/100% 100%;vertical-align:bottom;font-style:normal;box-shadow:0 0 1px 1px rgba(0,0,0,.1);border-radius:2px}.flag-ad{background-image:url(/static/media/ad.30f99f82.svg)}.flag-ae{background-image:url(/static/media/ae.1f331bd9.svg)}.flag-af{background-image:url(/static/media/af.a8df755f.svg)}.flag-ag{background-image:url(/static/media/ag.7cb635f0.svg)}.flag-ai{background-image:url(/static/media/ai.928b5a4f.svg)}.flag-al{background-image:url(/static/media/al.1c4942df.svg)}.flag-am{background-image:url(/static/media/am.af917f4b.svg)}.flag-ao{background-image:url(/static/media/ao.fd948d03.svg)}.flag-aq{background-image:url(/static/media/aq.fb98f0e6.svg)}.flag-ar{background-image:url(/static/media/ar.2ed2ee2a.svg)}.flag-as{background-image:url(/static/media/as.e18a5953.svg)}.flag-at{background-image:url(/static/media/at.511e196f.svg)}.flag-au{background-image:url(/static/media/au.b853c2eb.svg)}.flag-aw{background-image:url(/static/media/aw.dc91764d.svg)}.flag-ax{background-image:url(/static/media/ax.3301f616.svg)}.flag-az{background-image:url(/static/media/az.ba2d1e5e.svg)}.flag-ba{background-image:url(/static/media/ba.a441d8da.svg)}.flag-bb{background-image:url(/static/media/bb.c568edd5.svg)}.flag-bd{background-image:url(/static/media/bd.b12e3060.svg)}.flag-be{background-image:url(/static/media/be.fb18617c.svg)}.flag-bf{background-image:url(/static/media/bf.f88288fa.svg)}.flag-bg{background-image:url(/static/media/bg.bc04745d.svg)}.flag-bh{background-image:url(/static/media/bh.805f2682.svg)}.flag-bi{background-image:url(/static/media/bi.bc8085f9.svg)}.flag-bj{background-image:url(/static/media/bj.ea52986c.svg)}.flag-bl{background-image:url(/static/media/bl.a5c508b2.svg)}.flag-bm{background-image:url(/static/media/bm.6339387e.svg)}.flag-bn{background-image:url(/static/media/bn.51104069.svg)}.flag-bo{background-image:url(/static/media/bo.e02afe04.svg)}.flag-bq{background-image:url(/static/media/bq.4cac15ed.svg)}.flag-br{background-image:url(/static/media/br.fc7b8290.svg)}.flag-bs{background-image:url(/static/media/bs.421969c2.svg)}.flag-bt{background-image:url(/static/media/bt.39149c62.svg)}.flag-bv{background-image:url(/static/media/bv.58761e89.svg)}.flag-bw{background-image:url(/static/media/bw.8ecb0b8e.svg)}.flag-by{background-image:url(/static/media/by.6fd2caab.svg)}.flag-bz{background-image:url(/static/media/bz.e86e9bd2.svg)}.flag-ca{background-image:url(/static/media/ca.af259017.svg)}.flag-cc{background-image:url(/static/media/cc.ec7f3820.svg)}.flag-cd{background-image:url(/static/media/cd.020e3d1e.svg)}.flag-cf{background-image:url(/static/media/cf.f75250a7.svg)}.flag-cg{background-image:url(/static/media/cg.497d91d1.svg)}.flag-ch{background-image:url(/static/media/ch.d5161894.svg)}.flag-ci{background-image:url(/static/media/ci.1334b221.svg)}.flag-ck{background-image:url(/static/media/ck.869edc71.svg)}.flag-cl{background-image:url(/static/media/cl.9d5227cb.svg)}.flag-cm{background-image:url(/static/media/cm.17f2e2c9.svg)}.flag-cn{background-image:url(/static/media/cn.c2814ac0.svg)}.flag-co{background-image:url(/static/media/co.433d22ad.svg)}.flag-cr{background-image:url(/static/media/cr.20a9e6bf.svg)}.flag-cu{background-image:url(/static/media/cu.050058cb.svg)}.flag-cv{background-image:url(/static/media/cv.6b699492.svg)}.flag-cw{background-image:url(/static/media/cw.07a0d3f9.svg)}.flag-cx{background-image:url(/static/media/cx.5180dbe5.svg)}.flag-cy{background-image:url(/static/media/cy.657ef7aa.svg)}.flag-cz{background-image:url(/static/media/cz.6731f872.svg)}.flag-de{background-image:url(/static/media/de.01e89f77.svg)}.flag-dj{background-image:url(/static/media/dj.f4c086cc.svg)}.flag-dk{background-image:url(/static/media/dk.44761537.svg)}.flag-dm{background-image:url(/static/media/dm.89c91dc6.svg)}.flag-do{background-image:url(/static/media/do.d8ab6db9.svg)}.flag-dz{background-image:url(/static/media/dz.333db1ef.svg)}.flag-ec{background-image:url(/static/media/ec.918e57b8.svg)}.flag-ee{background-image:url(/static/media/ee.57f366b0.svg)}.flag-eg{background-image:url(/static/media/eg.07f2e96d.svg)}.flag-eh{background-image:url(/static/media/eh.e4f13505.svg)}.flag-er{background-image:url(/static/media/er.70738db6.svg)}.flag-es{background-image:url(/static/media/es.c6ca5440.svg)}.flag-et{background-image:url(/static/media/et.31aa0fc0.svg)}.flag-eu{background-image:url(/static/media/eu.17beaf81.svg)}.flag-fi{background-image:url(/static/media/fi.58bcc4af.svg)}.flag-fj{background-image:url(/static/media/fj.b1ddba60.svg)}.flag-fk{background-image:url(/static/media/fk.5c64395d.svg)}.flag-fm{background-image:url(/static/media/fm.2bd7d4df.svg)}.flag-fo{background-image:url(/static/media/fo.dc9ed815.svg)}.flag-fr{background-image:url(/static/media/fr.a178bcfb.svg)}.flag-ga{background-image:url(/static/media/ga.33442fb9.svg)}.flag-gb-eng{background-image:url(/static/media/gb-eng.a933214c.svg)}.flag-gb-nir{background-image:url(/static/media/gb-nir.943d406a.svg)}.flag-gb-sct{background-image:url(/static/media/gb-sct.772350bf.svg)}.flag-gb-wls{background-image:url(/static/media/gb-wls.2831a6dd.svg)}.flag-gb{background-image:url(/static/media/gb.5638bbd9.svg)}.flag-gd{background-image:url(/static/media/gd.c17d779e.svg)}.flag-ge{background-image:url(/static/media/ge.334a8275.svg)}.flag-gf{background-image:url(/static/media/gf.4ea8e159.svg)}.flag-gg{background-image:url(/static/media/gg.d339aeb2.svg)}.flag-gh{background-image:url(/static/media/gh.d4b35e14.svg)}.flag-gi{background-image:url(/static/media/gi.c9543d40.svg)}.flag-gl{background-image:url(/static/media/gl.d02c42ea.svg)}.flag-gm{background-image:url(/static/media/gm.9423800e.svg)}.flag-gn{background-image:url(/static/media/gn.e472dff7.svg)}.flag-gp{background-image:url(/static/media/gp.a178bcfb.svg)}.flag-gq{background-image:url(/static/media/gq.6bbb0e76.svg)}.flag-gr{background-image:url(/static/media/gr.9a9a62a1.svg)}.flag-gs{background-image:url(/static/media/gs.37216917.svg)}.flag-gt{background-image:url(/static/media/gt.0b689ffe.svg)}.flag-gu{background-image:url(/static/media/gu.ad34e604.svg)}.flag-gw{background-image:url(/static/media/gw.e1d47aa4.svg)}.flag-gy{background-image:url(/static/media/gy.19bcfc34.svg)}.flag-hk{background-image:url(/static/media/hk.fb606eb1.svg)}.flag-hm{background-image:url(/static/media/hm.b43f3857.svg)}.flag-hn{background-image:url(/static/media/hn.3d726baa.svg)}.flag-hr{background-image:url(/static/media/hr.79e564a4.svg)}.flag-ht{background-image:url(/static/media/ht.d0404e4a.svg)}.flag-hu{background-image:url(/static/media/hu.a8abaf37.svg)}.flag-id{background-image:url(/static/media/id.ee020a0f.svg)}.flag-ie{background-image:url(/static/media/ie.d609c4e7.svg)}.flag-il{background-image:url(/static/media/il.0ea7e9da.svg)}.flag-im{background-image:url(/static/media/im.19884f0c.svg)}.flag-in{background-image:url(/static/media/in.2d667fbb.svg)}.flag-io{background-image:url(/static/media/io.2e0c61df.svg)}.flag-iq{background-image:url(/static/media/iq.61fca184.svg)}.flag-ir{background-image:url(/static/media/ir.3cb275a7.svg)}.flag-is{background-image:url(/static/media/is.ec1fb876.svg)}.flag-it{background-image:url(/static/media/it.bd6b5ff3.svg)}.flag-je{background-image:url(/static/media/je.6a9e1b93.svg)}.flag-jm{background-image:url(/static/media/jm.7db0ffd8.svg)}.flag-jo{background-image:url(/static/media/jo.d1405940.svg)}.flag-jp{background-image:url(/static/media/jp.fd264681.svg)}.flag-ke{background-image:url(/static/media/ke.15b698f3.svg)}.flag-kg{background-image:url(/static/media/kg.de33c048.svg)}.flag-kh{background-image:url(/static/media/kh.bfffb443.svg)}.flag-ki{background-image:url(/static/media/ki.fbe824dc.svg)}.flag-km{background-image:url(/static/media/km.cd351374.svg)}.flag-kn{background-image:url(/static/media/kn.7ab9462c.svg)}.flag-kp{background-image:url(/static/media/kp.b2729dfa.svg)}.flag-kr{background-image:url(/static/media/kr.32f23faf.svg)}.flag-kw{background-image:url(/static/media/kw.3e24a94a.svg)}.flag-ky{background-image:url(/static/media/ky.f7c3a515.svg)}.flag-kz{background-image:url(/static/media/kz.529db212.svg)}.flag-la{background-image:url(/static/media/la.bdfc4ab5.svg)}.flag-lb{background-image:url(/static/media/lb.49819740.svg)}.flag-lc{background-image:url(/static/media/lc.6c2940da.svg)}.flag-li{background-image:url(/static/media/li.10e0d5b2.svg)}.flag-lk{background-image:url(/static/media/lk.f0a4f4f6.svg)}.flag-lr{background-image:url(/static/media/lr.5485e606.svg)}.flag-ls{background-image:url(/static/media/ls.700ddad0.svg)}.flag-lt{background-image:url(/static/media/lt.14b63eab.svg)}.flag-lu{background-image:url(/static/media/lu.06956a13.svg)}.flag-lv{background-image:url(/static/media/lv.83353fa9.svg)}.flag-ly{background-image:url(/static/media/ly.ededce32.svg)}.flag-ma{background-image:url(/static/media/ma.8c27c493.svg)}.flag-mc{background-image:url(/static/media/mc.4241d3ff.svg)}.flag-md{background-image:url(/static/media/md.f9aceffb.svg)}.flag-me{background-image:url(/static/media/me.399015d8.svg)}.flag-mf{background-image:url(/static/media/mf.a178bcfb.svg)}.flag-mg{background-image:url(/static/media/mg.0c0da5f0.svg)}.flag-mh{background-image:url(/static/media/mh.a3bb001b.svg)}.flag-mk{background-image:url(/static/media/mk.29cb0cb2.svg)}.flag-ml{background-image:url(/static/media/ml.be076fd9.svg)}.flag-mm{background-image:url(/static/media/mm.e6d7c5a4.svg)}.flag-mn{background-image:url(/static/media/mn.cfd48e45.svg)}.flag-mo{background-image:url(/static/media/mo.36f1d6f2.svg)}.flag-mp{background-image:url(/static/media/mp.fcdc8e39.svg)}.flag-mq{background-image:url(/static/media/mq.4c4286cd.svg)}.flag-mr{background-image:url(/static/media/mr.6b3d082d.svg)}.flag-ms{background-image:url(/static/media/ms.8b73c710.svg)}.flag-mt{background-image:url(/static/media/mt.cffcad79.svg)}.flag-mu{background-image:url(/static/media/mu.974b9e6c.svg)}.flag-mv{background-image:url(/static/media/mv.e343afe8.svg)}.flag-mw{background-image:url(/static/media/mw.5b33db84.svg)}.flag-mx{background-image:url(/static/media/mx.184d53d1.svg)}.flag-my{background-image:url(/static/media/my.aae5bd9c.svg)}.flag-mz{background-image:url(/static/media/mz.cd1e97af.svg)}.flag-na{background-image:url(/static/media/na.f38aead1.svg)}.flag-nc{background-image:url(/static/media/nc.a2dc6650.svg)}.flag-ne{background-image:url(/static/media/ne.bad21adc.svg)}.flag-nf{background-image:url(/static/media/nf.fc2d0f07.svg)}.flag-ng{background-image:url(/static/media/ng.2ddc320b.svg)}.flag-ni{background-image:url(/static/media/ni.2b983496.svg)}.flag-nl{background-image:url(/static/media/nl.de2a39a2.svg)}.flag-no{background-image:url(/static/media/no.8331157c.svg)}.flag-np{background-image:url(/static/media/np.e6de6946.svg)}.flag-nr{background-image:url(/static/media/nr.f2afa5b9.svg)}.flag-nu{background-image:url(/static/media/nu.e6bfaa15.svg)}.flag-nz{background-image:url(/static/media/nz.03d7410a.svg)}.flag-om{background-image:url(/static/media/om.9b7a06b9.svg)}.flag-pa{background-image:url(/static/media/pa.91076135.svg)}.flag-pe{background-image:url(/static/media/pe.4cabbfc6.svg)}.flag-pf{background-image:url(/static/media/pf.28a15c37.svg)}.flag-pg{background-image:url(/static/media/pg.e444f903.svg)}.flag-ph{background-image:url(/static/media/ph.8b5fbe69.svg)}.flag-pk{background-image:url(/static/media/pk.db891066.svg)}.flag-pl{background-image:url(/static/media/pl.2257cff6.svg)}.flag-pm{background-image:url(/static/media/pm.a2dc6650.svg)}.flag-pn{background-image:url(/static/media/pn.bf813bfe.svg)}.flag-pr{background-image:url(/static/media/pr.e489537c.svg)}.flag-ps{background-image:url(/static/media/ps.225ede35.svg)}.flag-pt{background-image:url(/static/media/pt.e129260b.svg)}.flag-pw{background-image:url(/static/media/pw.0557592e.svg)}.flag-py{background-image:url(/static/media/py.abc5b396.svg)}.flag-qa{background-image:url(/static/media/qa.20a4d741.svg)}.flag-re{background-image:url(/static/media/re.a2dc6650.svg)}.flag-ro{background-image:url(/static/media/ro.552b5d97.svg)}.flag-rs{background-image:url(/static/media/rs.426b1d47.svg)}.flag-ru{background-image:url(/static/media/ru.517e32a1.svg)}.flag-rw{background-image:url(/static/media/rw.46fb809f.svg)}.flag-sa{background-image:url(/static/media/sa.67b058ae.svg)}.flag-sb{background-image:url(/static/media/sb.115ce3e5.svg)}.flag-sc{background-image:url(/static/media/sc.fdc11a48.svg)}.flag-sd{background-image:url(/static/media/sd.a14badd5.svg)}.flag-se{background-image:url(/static/media/se.22475f52.svg)}.flag-sg{background-image:url(/static/media/sg.22b0739e.svg)}.flag-sh{background-image:url(/static/media/sh.0726abdb.svg)}.flag-si{background-image:url(/static/media/si.72f83c29.svg)}.flag-sj{background-image:url(/static/media/sj.8331157c.svg)}.flag-sk{background-image:url(/static/media/sk.f44daf85.svg)}.flag-sl{background-image:url(/static/media/sl.835d44f6.svg)}.flag-sm{background-image:url(/static/media/sm.f3eb4474.svg)}.flag-sn{background-image:url(/static/media/sn.4dc603d1.svg)}.flag-so{background-image:url(/static/media/so.3bdb1de2.svg)}.flag-sr{background-image:url(/static/media/sr.65cdb1de.svg)}.flag-ss{background-image:url(/static/media/ss.0c7c9ffc.svg)}.flag-st{background-image:url(/static/media/st.230410b5.svg)}.flag-sv{background-image:url(/static/media/sv.a21150d5.svg)}.flag-sx{background-image:url(/static/media/sx.d23d1807.svg)}.flag-sy{background-image:url(/static/media/sy.0fedea07.svg)}.flag-sz{background-image:url(/static/media/sz.1ae99e45.svg)}.flag-tc{background-image:url(/static/media/tc.2f7d308e.svg)}.flag-td{background-image:url(/static/media/td.079a2525.svg)}.flag-tf{background-image:url(/static/media/tf.adc24fb2.svg)}.flag-tg{background-image:url(/static/media/tg.b96ee542.svg)}.flag-th{background-image:url(/static/media/th.50269587.svg)}.flag-tj{background-image:url(/static/media/tj.b6533ad3.svg)}.flag-tk{background-image:url(/static/media/tk.22d4831b.svg)}.flag-tl{background-image:url(/static/media/tl.f563fdae.svg)}.flag-tm{background-image:url(/static/media/tm.d2132088.svg)}.flag-tn{background-image:url(/static/media/tn.ef273685.svg)}.flag-to{background-image:url(/static/media/to.fa884203.svg)}.flag-tr{background-image:url(/static/media/tr.aabe02c2.svg)}.flag-tt{background-image:url(/static/media/tt.f09daa6d.svg)}.flag-tv{background-image:url(/static/media/tv.1a077ad0.svg)}.flag-tw{background-image:url(/static/media/tw.7baefd1c.svg)}.flag-tz{background-image:url(/static/media/tz.d5c9c20a.svg)}.flag-ua{background-image:url(/static/media/ua.acc88be0.svg)}.flag-ug{background-image:url(/static/media/ug.1e070275.svg)}.flag-um{background-image:url(/static/media/um.a1fa2de3.svg)}.flag-un{background-image:url(/static/media/un.1519b6c6.svg)}.flag-us{background-image:url(/static/media/us.2382ea7e.svg)}.flag-uy{background-image:url(/static/media/uy.a7e91b40.svg)}.flag-uz{background-image:url(/static/media/uz.791dfbda.svg)}.flag-va{background-image:url(/static/media/va.6b139c75.svg)}.flag-vc{background-image:url(/static/media/vc.f3912357.svg)}.flag-ve{background-image:url(/static/media/ve.6f48a1b9.svg)}.flag-vg{background-image:url(/static/media/vg.3b3121b2.svg)}.flag-vi{background-image:url(/static/media/vi.b3c0a20f.svg)}.flag-vn{background-image:url(/static/media/vn.0b7571b8.svg)}.flag-vu{background-image:url(/static/media/vu.9a6c3abc.svg)}.flag-wf{background-image:url(/static/media/wf.4b4f5462.svg)}.flag-ws{background-image:url(/static/media/ws.23b64335.svg)}.flag-ye{background-image:url(/static/media/ye.55897575.svg)}.flag-yt{background-image:url(/static/media/yt.a2dc6650.svg)}.flag-za{background-image:url(/static/media/za.d8ffed67.svg)}.flag-zm{background-image:url(/static/media/zm.62586634.svg)}.flag-zw{background-image:url(/static/media/zw.e223cee5.svg)}.payment{width:2.5rem;height:1.5rem;display:inline-block;background:no-repeat 50%/100% 100%;vertical-align:bottom;font-style:normal;box-shadow:0 0 1px 1px rgba(0,0,0,.1);border-radius:2px}.payment-2checkout-dark{background-image:url(/static/media/2checkout-dark.65d58d80.svg)}.payment-2checkout{background-image:url(/static/media/2checkout.e14c0f5e.svg)}.payment-alipay-dark{background-image:url(/static/media/alipay-dark.b6a651d2.svg)}.payment-alipay{background-image:url(/static/media/alipay.31580e28.svg)}.payment-amazon-dark{background-image:url(/static/media/amazon-dark.b178a57f.svg)}.payment-amazon{background-image:url(/static/media/amazon.5c500045.svg)}.payment-americanexpress-dark{background-image:url(/static/media/americanexpress-dark.c2ea2d77.svg)}.payment-americanexpress{background-image:url(/static/media/americanexpress.b89abdaf.svg)}.payment-applepay-dark{background-image:url(/static/media/applepay-dark.e044dbdb.svg)}.payment-applepay{background-image:url(/static/media/applepay.1ff3d3f0.svg)}.payment-bancontact-dark{background-image:url(/static/media/bancontact-dark.6e786090.svg)}.payment-bancontact{background-image:url(/static/media/bancontact.8c0a0fa2.svg)}.payment-bitcoin-dark{background-image:url(/static/media/bitcoin-dark.edaf60e1.svg)}.payment-bitcoin{background-image:url(/static/media/bitcoin.d9ac7b61.svg)}.payment-bitpay-dark{background-image:url(/static/media/bitpay-dark.f86a15da.svg)}.payment-bitpay{background-image:url(/static/media/bitpay.ffb94e65.svg)}.payment-cirrus-dark{background-image:url(/static/media/cirrus-dark.243a362e.svg)}.payment-cirrus{background-image:url(/static/media/cirrus.983db5f2.svg)}.payment-clickandbuy-dark{background-image:url(/static/media/clickandbuy-dark.f7d38984.svg)}.payment-clickandbuy{background-image:url(/static/media/clickandbuy.eb61d075.svg)}.payment-coinkite-dark{background-image:url(/static/media/coinkite-dark.f50deb17.svg)}.payment-coinkite{background-image:url(/static/media/coinkite.b6098277.svg)}.payment-dinersclub-dark{background-image:url(/static/media/dinersclub-dark.baff56e3.svg)}.payment-dinersclub{background-image:url(/static/media/dinersclub.45249b1d.svg)}.payment-directdebit-dark{background-image:url(/static/media/directdebit-dark.bf510996.svg)}.payment-directdebit{background-image:url(/static/media/directdebit.37695b62.svg)}.payment-discover-dark{background-image:url(/static/media/discover-dark.00f5c21f.svg)}.payment-discover{background-image:url(/static/media/discover.2f4fe159.svg)}.payment-dwolla-dark{background-image:url(/static/media/dwolla-dark.ccae2767.svg)}.payment-dwolla{background-image:url(/static/media/dwolla.36f57770.svg)}.payment-ebay-dark{background-image:url(/static/media/ebay-dark.bd7ccde1.svg)}.payment-ebay{background-image:url(/static/media/ebay.862b611a.svg)}.payment-eway-dark{background-image:url(/static/media/eway-dark.bbf15466.svg)}.payment-eway{background-image:url(/static/media/eway.54d6e672.svg)}.payment-giropay-dark{background-image:url(/static/media/giropay-dark.ff3c753a.svg)}.payment-giropay{background-image:url(/static/media/giropay.7337d9d0.svg)}.payment-googlewallet-dark{background-image:url(/static/media/googlewallet-dark.7cbe03be.svg)}.payment-googlewallet{background-image:url(/static/media/googlewallet.7f0e39ad.svg)}.payment-ingenico-dark{background-image:url(/static/media/ingenico-dark.5bef3895.svg)}.payment-ingenico{background-image:url(/static/media/ingenico.20a24d68.svg)}.payment-jcb-dark{background-image:url(/static/media/jcb-dark.f9bf701d.svg)}.payment-jcb{background-image:url(/static/media/jcb.2646bc51.svg)}.payment-klarna-dark{background-image:url(/static/media/klarna-dark.3a666a1e.svg)}.payment-klarna{background-image:url(/static/media/klarna.c05b3bba.svg)}.payment-laser-dark{background-image:url(/static/media/laser-dark.758bd7b6.svg)}.payment-laser{background-image:url(/static/media/laser.4642dfb3.svg)}.payment-maestro-dark{background-image:url(/static/media/maestro-dark.0d91ff8f.svg)}.payment-maestro{background-image:url(/static/media/maestro.31a202b4.svg)}.payment-mastercard-dark{background-image:url(/static/media/mastercard-dark.b1695f2b.svg)}.payment-mastercard{background-image:url(/static/media/mastercard.a6684d93.svg)}.payment-monero-dark{background-image:url(/static/media/monero-dark.29d40dee.svg)}.payment-monero{background-image:url(/static/media/monero.7df16d08.svg)}.payment-neteller-dark{background-image:url(/static/media/neteller-dark.63736cac.svg)}.payment-neteller{background-image:url(/static/media/neteller.798e0b4b.svg)}.payment-ogone-dark{background-image:url(/static/media/ogone-dark.5fa709fb.svg)}.payment-ogone{background-image:url(/static/media/ogone.8832c251.svg)}.payment-okpay-dark{background-image:url(/static/media/okpay-dark.26eabf7a.svg)}.payment-okpay{background-image:url(/static/media/okpay.72f763a2.svg)}.payment-paybox-dark{background-image:url(/static/media/paybox-dark.321bd555.svg)}.payment-paybox{background-image:url(/static/media/paybox.46f8af3b.svg)}.payment-paymill-dark{background-image:url(/static/media/paymill-dark.d8737b88.svg)}.payment-paymill{background-image:url(/static/media/paymill.6f906616.svg)}.payment-payone-dark{background-image:url(/static/media/payone-dark.992480f1.svg)}.payment-payone{background-image:url(/static/media/payone.2c68e11e.svg)}.payment-payoneer-dark{background-image:url(/static/media/payoneer-dark.8d95de50.svg)}.payment-payoneer{background-image:url(/static/media/payoneer.e460ab6b.svg)}.payment-paypal-dark{background-image:url(/static/media/paypal-dark.2abbaed4.svg)}.payment-paypal{background-image:url(/static/media/paypal.aa9749d2.svg)}.payment-paysafecard-dark{background-image:url(/static/media/paysafecard-dark.2a3832c3.svg)}.payment-paysafecard{background-image:url(/static/media/paysafecard.0db2bc55.svg)}.payment-payu-dark{background-image:url(/static/media/payu-dark.80265cc7.svg)}.payment-payu{background-image:url(/static/media/payu.ece9e639.svg)}.payment-payza-dark{background-image:url(/static/media/payza-dark.aaf8d63f.svg)}.payment-payza{background-image:url(/static/media/payza.05716451.svg)}.payment-ripple-dark{background-image:url(/static/media/ripple-dark.a741b2b1.svg)}.payment-ripple{background-image:url(/static/media/ripple.44f32f32.svg)}.payment-sage-dark{background-image:url(/static/media/sage-dark.1560c69d.svg)}.payment-sage{background-image:url(/static/media/sage.c962e60b.svg)}.payment-sepa-dark{background-image:url(/static/media/sepa-dark.3834e619.svg)}.payment-sepa{background-image:url(/static/media/sepa.45d27bde.svg)}.payment-shopify-dark{background-image:url(/static/media/shopify-dark.937412fd.svg)}.payment-shopify{background-image:url(/static/media/shopify.2a87d23f.svg)}.payment-skrill-dark{background-image:url(/static/media/skrill-dark.a1a4a38c.svg)}.payment-skrill{background-image:url(/static/media/skrill.b0d31271.svg)}.payment-solo-dark{background-image:url(/static/media/solo-dark.17da28b9.svg)}.payment-solo{background-image:url(/static/media/solo.f7fcc525.svg)}.payment-square-dark{background-image:url(/static/media/square-dark.4db9c83c.svg)}.payment-square{background-image:url(/static/media/square.48f11398.svg)}.payment-stripe-dark{background-image:url(/static/media/stripe-dark.025afc35.svg)}.payment-stripe{background-image:url(/static/media/stripe.77c6af28.svg)}.payment-switch-dark{background-image:url(/static/media/switch-dark.54599ad9.svg)}.payment-switch{background-image:url(/static/media/switch.c1a0e47d.svg)}.payment-ukash-dark{background-image:url(/static/media/ukash-dark.89b7d2ae.svg)}.payment-ukash{background-image:url(/static/media/ukash.7a542b9e.svg)}.payment-unionpay-dark{background-image:url(/static/media/unionpay-dark.22beb1a2.svg)}.payment-unionpay{background-image:url(/static/media/unionpay.285de38e.svg)}.payment-verifone-dark{background-image:url(/static/media/verifone-dark.e7b2a0bc.svg)}.payment-verifone{background-image:url(/static/media/verifone.012caff4.svg)}.payment-verisign-dark{background-image:url(/static/media/verisign-dark.1f0c2c56.svg)}.payment-verisign{background-image:url(/static/media/verisign.3684cf82.svg)}.payment-visa-dark{background-image:url(/static/media/visa-dark.f6a55e1d.svg)}.payment-visa{background-image:url(/static/media/visa.a09152e7.svg)}.payment-webmoney-dark{background-image:url(/static/media/webmoney-dark.5c559c4c.svg)}.payment-webmoney{background-image:url(/static/media/webmoney.c77724f3.svg)}.payment-westernunion-dark{background-image:url(/static/media/westernunion-dark.5f3974a3.svg)}.payment-westernunion{background-image:url(/static/media/westernunion.4082e1b1.svg)}.payment-worldpay-dark{background-image:url(/static/media/worldpay-dark.a99e6d1c.svg)}.payment-worldpay{background-image:url(/static/media/worldpay.d63620a3.svg)}svg{touch-action:none}.jvectormap-container{width:100%;height:100%;position:relative;overflow:hidden;touch-action:none}.jvectormap-tip{position:absolute;display:none;border-radius:3px;background:#212529;color:#fff;padding:6px;font-size:11px;line-height:1;font-weight:700}.jvectormap-tip small{font-size:inherit;font-weight:400}.jvectormap-goback,.jvectormap-zoomin,.jvectormap-zoomout{position:absolute;left:10px;border-radius:3px;background:#292929;padding:3px;color:#fff;cursor:pointer;line-height:10px;text-align:center;box-sizing:initial}.jvectormap-zoomin,.jvectormap-zoomout{width:10px;height:10px}.jvectormap-zoomin{top:10px}.jvectormap-zoomout{top:30px}.jvectormap-goback{bottom:10px;z-index:1000;padding:6px}.jvectormap-spinner{position:absolute;left:0;top:0;right:0;bottom:0;background:url(data:image/gif;base64,R0lGODlhIAAgAPMAAP///wAAAMbGxoSEhLa2tpqamjY2NlZWVtjY2OTk5Ly8vB4eHgQEBAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAIAAgAAAE5xDISWlhperN52JLhSSdRgwVo1ICQZRUsiwHpTJT4iowNS8vyW2icCF6k8HMMBkCEDskxTBDAZwuAkkqIfxIQyhBQBFvAQSDITM5VDW6XNE4KagNh6Bgwe60smQUB3d4Rz1ZBApnFASDd0hihh12BkE9kjAJVlycXIg7CQIFA6SlnJ87paqbSKiKoqusnbMdmDC2tXQlkUhziYtyWTxIfy6BE8WJt5YJvpJivxNaGmLHT0VnOgSYf0dZXS7APdpB309RnHOG5gDqXGLDaC457D1zZ/V/nmOM82XiHRLYKhKP1oZmADdEAAAh+QQJCgAAACwAAAAAIAAgAAAE6hDISWlZpOrNp1lGNRSdRpDUolIGw5RUYhhHukqFu8DsrEyqnWThGvAmhVlteBvojpTDDBUEIFwMFBRAmBkSgOrBFZogCASwBDEY/CZSg7GSE0gSCjQBMVG023xWBhklAnoEdhQEfyNqMIcKjhRsjEdnezB+A4k8gTwJhFuiW4dokXiloUepBAp5qaKpp6+Ho7aWW54wl7obvEe0kRuoplCGepwSx2jJvqHEmGt6whJpGpfJCHmOoNHKaHx61WiSR92E4lbFoq+B6QDtuetcaBPnW6+O7wDHpIiK9SaVK5GgV543tzjgGcghAgAh+QQJCgAAACwAAAAAIAAgAAAE7hDISSkxpOrN5zFHNWRdhSiVoVLHspRUMoyUakyEe8PTPCATW9A14E0UvuAKMNAZKYUZCiBMuBakSQKG8G2FzUWox2AUtAQFcBKlVQoLgQReZhQlCIJesQXI5B0CBnUMOxMCenoCfTCEWBsJColTMANldx15BGs8B5wlCZ9Po6OJkwmRpnqkqnuSrayqfKmqpLajoiW5HJq7FL1Gr2mMMcKUMIiJgIemy7xZtJsTmsM4xHiKv5KMCXqfyUCJEonXPN2rAOIAmsfB3uPoAK++G+w48edZPK+M6hLJpQg484enXIdQFSS1u6UhksENEQAAIfkECQoAAAAsAAAAACAAIAAABOcQyEmpGKLqzWcZRVUQnZYg1aBSh2GUVEIQ2aQOE+G+cD4ntpWkZQj1JIiZIogDFFyHI0UxQwFugMSOFIPJftfVAEoZLBbcLEFhlQiqGp1Vd140AUklUN3eCA51C1EWMzMCezCBBmkxVIVHBWd3HHl9JQOIJSdSnJ0TDKChCwUJjoWMPaGqDKannasMo6WnM562R5YluZRwur0wpgqZE7NKUm+FNRPIhjBJxKZteWuIBMN4zRMIVIhffcgojwCF117i4nlLnY5ztRLsnOk+aV+oJY7V7m76PdkS4trKcdg0Zc0tTcKkRAAAIfkECQoAAAAsAAAAACAAIAAABO4QyEkpKqjqzScpRaVkXZWQEximw1BSCUEIlDohrft6cpKCk5xid5MNJTaAIkekKGQkWyKHkvhKsR7ARmitkAYDYRIbUQRQjWBwJRzChi9CRlBcY1UN4g0/VNB0AlcvcAYHRyZPdEQFYV8ccwR5HWxEJ02YmRMLnJ1xCYp0Y5idpQuhopmmC2KgojKasUQDk5BNAwwMOh2RtRq5uQuPZKGIJQIGwAwGf6I0JXMpC8C7kXWDBINFMxS4DKMAWVWAGYsAdNqW5uaRxkSKJOZKaU3tPOBZ4DuK2LATgJhkPJMgTwKCdFjyPHEnKxFCDhEAACH5BAkKAAAALAAAAAAgACAAAATzEMhJaVKp6s2nIkolIJ2WkBShpkVRWqqQrhLSEu9MZJKK9y1ZrqYK9WiClmvoUaF8gIQSNeF1Er4MNFn4SRSDARWroAIETg1iVwuHjYB1kYc1mwruwXKC9gmsJXliGxc+XiUCby9ydh1sOSdMkpMTBpaXBzsfhoc5l58Gm5yToAaZhaOUqjkDgCWNHAULCwOLaTmzswadEqggQwgHuQsHIoZCHQMMQgQGubVEcxOPFAcMDAYUA85eWARmfSRQCdcMe0zeP1AAygwLlJtPNAAL19DARdPzBOWSm1brJBi45soRAWQAAkrQIykShQ9wVhHCwCQCACH5BAkKAAAALAAAAAAgACAAAATrEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq+E71SRQeyqUToLA7VxF0JDyIQh/MVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiRMDjI0Fd30/iI2UA5GSS5UDj2l6NoqgOgN4gksEBgYFf0FDqKgHnyZ9OX8HrgYHdHpcHQULXAS2qKpENRg7eAMLC7kTBaixUYFkKAzWAAnLC7FLVxLWDBLKCwaKTULgEwbLA4hJtOkSBNqITT3xEgfLpBtzE/jiuL04RGEBgwWhShRgQExHBAAh+QQJCgAAACwAAAAAIAAgAAAE7xDISWlSqerNpyJKhWRdlSAVoVLCWk6JKlAqAavhO9UkUHsqlE6CwO1cRdCQ8iEIfzFVTzLdRAmZX3I2SfZiCqGk5dTESJeaOAlClzsJsqwiJwiqnFrb2nS9kmIcgEsjQydLiIlHehhpejaIjzh9eomSjZR+ipslWIRLAgMDOR2DOqKogTB9pCUJBagDBXR6XB0EBkIIsaRsGGMMAxoDBgYHTKJiUYEGDAzHC9EACcUGkIgFzgwZ0QsSBcXHiQvOwgDdEwfFs0sDzt4S6BK4xYjkDOzn0unFeBzOBijIm1Dgmg5YFQwsCMjp1oJ8LyIAACH5BAkKAAAALAAAAAAgACAAAATwEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq+E71SRQeyqUToLA7VxF0JDyIQh/MVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiUd6GGl6NoiPOH16iZKNlH6KmyWFOggHhEEvAwwMA0N9GBsEC6amhnVcEwavDAazGwIDaH1ipaYLBUTCGgQDA8NdHz0FpqgTBwsLqAbWAAnIA4FWKdMLGdYGEgraigbT0OITBcg5QwPT4xLrROZL6AuQAPUS7bxLpoWidY0JtxLHKhwwMJBTHgPKdEQAACH5BAkKAAAALAAAAAAgACAAAATrEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq+E71SRQeyqUToLA7VxF0JDyIQh/MVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiUd6GAULDJCRiXo1CpGXDJOUjY+Yip9DhToJA4RBLwMLCwVDfRgbBAaqqoZ1XBMHswsHtxtFaH1iqaoGNgAIxRpbFAgfPQSqpbgGBqUD1wBXeCYp1AYZ19JJOYgH1KwA4UBvQwXUBxPqVD9L3sbp2BNk2xvvFPJd+MFCN6HAAIKgNggY0KtEBAAh+QQJCgAAACwAAAAAIAAgAAAE6BDISWlSqerNpyJKhWRdlSAVoVLCWk6JKlAqAavhO9UkUHsqlE6CwO1cRdCQ8iEIfzFVTzLdRAmZX3I2SfYIDMaAFdTESJeaEDAIMxYFqrOUaNW4E4ObYcCXaiBVEgULe0NJaxxtYksjh2NLkZISgDgJhHthkpU4mW6blRiYmZOlh4JWkDqILwUGBnE6TYEbCgevr0N1gH4At7gHiRpFaLNrrq8HNgAJA70AWxQIH1+vsYMDAzZQPC9VCNkDWUhGkuE5PxJNwiUK4UfLzOlD4WvzAHaoG9nxPi5d+jYUqfAhhykOFwJWiAAAIfkECQoAAAAsAAAAACAAIAAABPAQyElpUqnqzaciSoVkXVUMFaFSwlpOCcMYlErAavhOMnNLNo8KsZsMZItJEIDIFSkLGQoQTNhIsFehRww2CQLKF0tYGKYSg+ygsZIuNqJksKgbfgIGepNo2cIUB3V1B3IvNiBYNQaDSTtfhhx0CwVPI0UJe0+bm4g5VgcGoqOcnjmjqDSdnhgEoamcsZuXO1aWQy8KAwOAuTYYGwi7w5h+Kr0SJ8MFihpNbx+4Erq7BYBuzsdiH1jCAzoSfl0rVirNbRXlBBlLX+BP0XJLAPGzTkAuAOqb0WT5AH7OcdCm5B8TgRwSRKIHQtaLCwg1RAAAOwAAAAAAAAAAAA==)}.jvectormap-legend-title{font-weight:700;font-size:14px;text-align:center}.jvectormap-legend-cnt{position:absolute}.jvectormap-legend-cnt-h{bottom:0;right:0}.jvectormap-legend-cnt-v{top:0;right:0}.jvectormap-legend{background:#000;color:#fff;border-radius:3px}.jvectormap-legend-cnt-h .jvectormap-legend{float:left;margin:0 10px 10px 0;padding:3px 3px 1px}.jvectormap-legend-cnt-h .jvectormap-legend .jvectormap-legend-tick{float:left}.jvectormap-legend-cnt-v .jvectormap-legend{margin:10px 10px 0 0;padding:3px}.jvectormap-legend-cnt-h .jvectormap-legend-tick{width:40px}.jvectormap-legend-cnt-h .jvectormap-legend-tick-sample{height:15px}.jvectormap-legend-cnt-v .jvectormap-legend-tick-sample{height:20px;width:20px;display:inline-block;vertical-align:middle}.jvectormap-legend-tick-text{font-size:12px}.jvectormap-legend-cnt-h .jvectormap-legend-tick-text{text-align:center}.jvectormap-legend-cnt-v .jvectormap-legend-tick-text{display:inline-block;vertical-align:middle;line-height:20px;padding-left:3px}.selectize-control.plugin-drag_drop.multi>.selectize-input>div.ui-sortable-placeholder{visibility:visible !important;background:#f2f2f2 !important;background:rgba(0,0,0,.06) !important;border:0 !important;box-shadow:inset 0 0 12px 4px #fff}.selectize-control.plugin-drag_drop .ui-sortable-placeholder:after{content:"!";visibility:hidden}.selectize-control.plugin-drag_drop .ui-sortable-helper{box-shadow:0 2px 5px rgba(0,0,0,.2)}.selectize-dropdown-header{position:relative;padding:5px 8px;border-bottom:1px solid #d0d0d0;background:#f8f8f8;border-radius:3px 3px 0 0}.selectize-dropdown-header-close{position:absolute;right:8px;top:50%;color:#495057;opacity:.4;margin-top:-12px;line-height:20px;font-size:20px !important}.selectize-dropdown-header-close:hover{color:#000}.selectize-dropdown.plugin-optgroup_columns .optgroup{border-right:1px solid #f2f2f2;border-top:0;float:left;box-sizing:border-box}.selectize-dropdown.plugin-optgroup_columns .optgroup:last-child{border-right:0}.selectize-dropdown.plugin-optgroup_columns .optgroup:before{display:none}.selectize-dropdown.plugin-optgroup_columns .optgroup-header{border-top:0}.selectize-control.plugin-remove_button [data-value]{position:relative;padding-right:24px !important}.selectize-control.plugin-remove_button [data-value] .remove{z-index:1;position:absolute;top:0;right:0;bottom:0;width:17px;text-align:center;font-weight:700;font-size:12px;color:inherit;text-decoration:none;vertical-align:middle;display:inline-block;padding:2px 0 0;border-left:1px solid #d0d0d0;border-radius:0 2px 2px 0;box-sizing:border-box}.selectize-control.plugin-remove_button [data-value] .remove:hover{background:rgba(0,0,0,.05)}.selectize-control.plugin-remove_button [data-value].active .remove{border-left-color:#cacaca}.selectize-control.plugin-remove_button .disabled [data-value] .remove:hover{background:0}.selectize-control.plugin-remove_button .disabled [data-value] .remove{border-left-color:#fff}.selectize-control.plugin-remove_button .remove-single{position:absolute;right:28px;top:6px;font-size:23px}.selectize-control{position:relative;padding:0;border:0}.selectize-dropdown,.selectize-input,.selectize-input input{color:#495057;font-family:inherit;font-size:15px;line-height:18px;-webkit-font-smoothing:inherit}.selectize-control.single .selectize-input.input-active,.selectize-input{background:#fff;cursor:text;display:inline-block}.selectize-input{border:1px solid rgba(0,40,100,.12);padding:.5625rem .75rem;display:inline-block;display:block;width:100%;overflow:hidden;position:relative;z-index:1;box-sizing:border-box;border-radius:3px;transition:border-color .3s,box-shadow .3s}.selectize-control.multi .selectize-input.has-items{padding:7px .75rem 4px 7px}.selectize-input.full{background-color:#fff}.selectize-input.disabled,.selectize-input.disabled *{cursor:default !important}.selectize-input.focus{border-color:#467fcf;box-shadow:0 0 0 2px rgba(70,127,207,.25)}.selectize-input.dropdown-active{border-radius:3px 3px 0 0}.selectize-input>*{vertical-align:initial;display:-moz-inline-stack;display:inline-block;zoom:1;*display:inline}.selectize-control.multi .selectize-input>div{cursor:pointer;margin:0 3px 3px 0;padding:2px 6px;background:#e9ecef;color:#495057;font-size:13px;border:0 solid rgba(0,40,100,.12);border-radius:3px;font-weight:400}.selectize-control.multi .selectize-input>div.active{background:#e8e8e8;color:#303030;border:0 solid #cacaca}.selectize-control.multi .selectize-input.disabled>div,.selectize-control.multi .selectize-input.disabled>div.active{color:#7d7d7d;background:#fff;border:0 solid #fff}.selectize-input>input{display:inline-block !important;padding:0 !important;min-height:0 !important;max-height:none !important;max-width:100% !important;margin:0 2px 0 0 !important;text-indent:0 !important;border:0 !important;background:none !important;line-height:inherit !important;box-shadow:none !important}.selectize-input>input::-ms-clear{display:none}.selectize-input>input:focus{outline:none !important}.selectize-input:after{content:" ";display:block;clear:left}.selectize-input.dropdown-active:before{content:" ";display:block;position:absolute;background:#f0f0f0;height:1px;bottom:0;left:0;right:0}.selectize-dropdown{position:absolute;z-index:10;background:#fff;margin:-1px 0 0;border:1px solid rgba(0,40,100,.12);border-top:0;box-sizing:border-box;border-radius:0 0 3px 3px;height:auto;padding:0}.selectize-dropdown [data-selectable]{cursor:pointer;overflow:hidden}.selectize-dropdown [data-selectable] .highlight{background:rgba(125,168,208,.2);border-radius:1px}.selectize-dropdown .optgroup-header,.selectize-dropdown [data-selectable]{padding:6px .75rem}.selectize-dropdown .optgroup:first-child .optgroup-header{border-top:0}.selectize-dropdown .optgroup-header{color:#495057;background:#fff;cursor:default}.selectize-dropdown .active{background-color:#f1f4f8;color:#467fcf}.selectize-dropdown .active.create{color:#495057}.selectize-dropdown .create{color:rgba(48,48,48,.5)}.selectize-dropdown-content{overflow-y:auto;overflow-x:hidden;max-height:200px;-webkit-overflow-scrolling:touch}.selectize-control.single .selectize-input,.selectize-control.single .selectize-input input{cursor:pointer}.selectize-control.single .selectize-input.input-active,.selectize-control.single .selectize-input.input-active input{cursor:text}.selectize-control.single .selectize-input:after{content:"";display:block;position:absolute;top:13px;right:12px;width:8px;height:10px;background:#fff url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 5'%3E%3Cpath fill='#999' d='M0 0L10 0L5 5L0 0'/%3E%3C/svg%3E") no-repeat 50%;background-size:8px 10px;transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s}.selectize-control.single .selectize-input.dropdown-active:after{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.selectize-control .selectize-input.disabled{opacity:.5;background-color:#fafafa}.selectize-dropdown .image,.selectize-input .image{width:1.25rem;height:1.25rem;background-size:contain;margin:-1px .5rem -1px -4px;line-height:1.25rem;float:left;display:flex;align-items:center;justify-content:center}.selectize-dropdown .image img,.selectize-input .image img{max-width:100%;box-shadow:0 1px 2px 0 rgba(0,0,0,.4);border-radius:2px}.selectize-input .image{width:1.5rem;height:1.5rem;margin:-3px .75rem -3px -5px}@font-face{font-family:feather;src:url(/static/media/feather-webfont.cc5143b2.eot);src:url(/static/media/feather-webfont.cc5143b2.eot#iefix) format("embedded-opentype"),url(/static/media/feather-webfont.2cf523cd.woff) format("woff"),url(/static/media/feather-webfont.b8e9cbc7.ttf) format("truetype"),url(/static/media/feather-webfont.4a878d5b.svg#feather) format("svg")}.fe{font-family:feather !important;speak:none;font-style:normal;font-weight:400;-webkit-font-feature-settings:normal;font-feature-settings:normal;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fe-activity:before{content:"\E900"}.fe-airplay:before{content:"\E901"}.fe-alert-circle:before{content:"\E902"}.fe-alert-octagon:before{content:"\E903"}.fe-alert-triangle:before{content:"\E904"}.fe-align-center:before{content:"\E905"}.fe-align-justify:before{content:"\E906"}.fe-align-left:before{content:"\E907"}.fe-align-right:before{content:"\E908"}.fe-anchor:before{content:"\E909"}.fe-aperture:before{content:"\E90A"}.fe-arrow-down:before{content:"\E90B"}.fe-arrow-down-circle:before{content:"\E90C"}.fe-arrow-down-left:before{content:"\E90D"}.fe-arrow-down-right:before{content:"\E90E"}.fe-arrow-left:before{content:"\E90F"}.fe-arrow-left-circle:before{content:"\E910"}.fe-arrow-right:before{content:"\E911"}.fe-arrow-right-circle:before{content:"\E912"}.fe-arrow-up:before{content:"\E913"}.fe-arrow-up-circle:before{content:"\E914"}.fe-arrow-up-left:before{content:"\E915"}.fe-arrow-up-right:before{content:"\E916"}.fe-at-sign:before{content:"\E917"}.fe-award:before{content:"\E918"}.fe-bar-chart:before{content:"\E919"}.fe-bar-chart-2:before{content:"\E91A"}.fe-battery:before{content:"\E91B"}.fe-battery-charging:before{content:"\E91C"}.fe-bell:before{content:"\E91D"}.fe-bell-off:before{content:"\E91E"}.fe-bluetooth:before{content:"\E91F"}.fe-bold:before{content:"\E920"}.fe-book:before{content:"\E921"}.fe-book-open:before{content:"\E922"}.fe-bookmark:before{content:"\E923"}.fe-box:before{content:"\E924"}.fe-briefcase:before{content:"\E925"}.fe-calendar:before{content:"\E926"}.fe-camera:before{content:"\E927"}.fe-camera-off:before{content:"\E928"}.fe-cast:before{content:"\E929"}.fe-check:before{content:"\E92A"}.fe-check-circle:before{content:"\E92B"}.fe-check-square:before{content:"\E92C"}.fe-chevron-down:before{content:"\E92D"}.fe-chevron-left:before{content:"\E92E"}.fe-chevron-right:before{content:"\E92F"}.fe-chevron-up:before{content:"\E930"}.fe-chevrons-down:before{content:"\E931"}.fe-chevrons-left:before{content:"\E932"}.fe-chevrons-right:before{content:"\E933"}.fe-chevrons-up:before{content:"\E934"}.fe-chrome:before{content:"\E935"}.fe-circle:before{content:"\E936"}.fe-clipboard:before{content:"\E937"}.fe-clock:before{content:"\E938"}.fe-cloud:before{content:"\E939"}.fe-cloud-drizzle:before{content:"\E93A"}.fe-cloud-lightning:before{content:"\E93B"}.fe-cloud-off:before{content:"\E93C"}.fe-cloud-rain:before{content:"\E93D"}.fe-cloud-snow:before{content:"\E93E"}.fe-code:before{content:"\E93F"}.fe-codepen:before{content:"\E940"}.fe-command:before{content:"\E941"}.fe-compass:before{content:"\E942"}.fe-copy:before{content:"\E943"}.fe-corner-down-left:before{content:"\E944"}.fe-corner-down-right:before{content:"\E945"}.fe-corner-left-down:before{content:"\E946"}.fe-corner-left-up:before{content:"\E947"}.fe-corner-right-down:before{content:"\E948"}.fe-corner-right-up:before{content:"\E949"}.fe-corner-up-left:before{content:"\E94A"}.fe-corner-up-right:before{content:"\E94B"}.fe-cpu:before{content:"\E94C"}.fe-credit-card:before{content:"\E94D"}.fe-crop:before{content:"\E94E"}.fe-crosshair:before{content:"\E94F"}.fe-database:before{content:"\E950"}.fe-delete:before{content:"\E951"}.fe-disc:before{content:"\E952"}.fe-dollar-sign:before{content:"\E953"}.fe-download:before{content:"\E954"}.fe-download-cloud:before{content:"\E955"}.fe-droplet:before{content:"\E956"}.fe-edit:before{content:"\E957"}.fe-edit-2:before{content:"\E958"}.fe-edit-3:before{content:"\E959"}.fe-external-link:before{content:"\E95A"}.fe-eye:before{content:"\E95B"}.fe-eye-off:before{content:"\E95C"}.fe-facebook:before{content:"\E95D"}.fe-fast-forward:before{content:"\E95E"}.fe-feather:before{content:"\E95F"}.fe-file:before{content:"\E960"}.fe-file-minus:before{content:"\E961"}.fe-file-plus:before{content:"\E962"}.fe-file-text:before{content:"\E963"}.fe-film:before{content:"\E964"}.fe-filter:before{content:"\E965"}.fe-flag:before{content:"\E966"}.fe-folder:before{content:"\E967"}.fe-folder-minus:before{content:"\E968"}.fe-folder-plus:before{content:"\E969"}.fe-git-branch:before{content:"\E96A"}.fe-git-commit:before{content:"\E96B"}.fe-git-merge:before{content:"\E96C"}.fe-git-pull-request:before{content:"\E96D"}.fe-github:before{content:"\E96E"}.fe-gitlab:before{content:"\E96F"}.fe-globe:before{content:"\E970"}.fe-grid:before{content:"\E971"}.fe-hard-drive:before{content:"\E972"}.fe-hash:before{content:"\E973"}.fe-headphones:before{content:"\E974"}.fe-heart:before{content:"\E975"}.fe-help-circle:before{content:"\E976"}.fe-home:before{content:"\E977"}.fe-image:before{content:"\E978"}.fe-inbox:before{content:"\E979"}.fe-info:before{content:"\E97A"}.fe-instagram:before{content:"\E97B"}.fe-italic:before{content:"\E97C"}.fe-layers:before{content:"\E97D"}.fe-layout:before{content:"\E97E"}.fe-life-buoy:before{content:"\E97F"}.fe-link:before{content:"\E980"}.fe-link-2:before{content:"\E981"}.fe-linkedin:before{content:"\E982"}.fe-list:before{content:"\E983"}.fe-loader:before{content:"\E984"}.fe-lock:before{content:"\E985"}.fe-log-in:before{content:"\E986"}.fe-log-out:before{content:"\E987"}.fe-mail:before{content:"\E988"}.fe-map:before{content:"\E989"}.fe-map-pin:before{content:"\E98A"}.fe-maximize:before{content:"\E98B"}.fe-maximize-2:before{content:"\E98C"}.fe-menu:before{content:"\E98D"}.fe-message-circle:before{content:"\E98E"}.fe-message-square:before{content:"\E98F"}.fe-mic:before{content:"\E990"}.fe-mic-off:before{content:"\E991"}.fe-minimize:before{content:"\E992"}.fe-minimize-2:before{content:"\E993"}.fe-minus:before{content:"\E994"}.fe-minus-circle:before{content:"\E995"}.fe-minus-square:before{content:"\E996"}.fe-monitor:before{content:"\E997"}.fe-moon:before{content:"\E998"}.fe-more-horizontal:before{content:"\E999"}.fe-more-vertical:before{content:"\E99A"}.fe-move:before{content:"\E99B"}.fe-music:before{content:"\E99C"}.fe-navigation:before{content:"\E99D"}.fe-navigation-2:before{content:"\E99E"}.fe-octagon:before{content:"\E99F"}.fe-package:before{content:"\E9A0"}.fe-paperclip:before{content:"\E9A1"}.fe-pause:before{content:"\E9A2"}.fe-pause-circle:before{content:"\E9A3"}.fe-percent:before{content:"\E9A4"}.fe-phone:before{content:"\E9A5"}.fe-phone-call:before{content:"\E9A6"}.fe-phone-forwarded:before{content:"\E9A7"}.fe-phone-incoming:before{content:"\E9A8"}.fe-phone-missed:before{content:"\E9A9"}.fe-phone-off:before{content:"\E9AA"}.fe-phone-outgoing:before{content:"\E9AB"}.fe-pie-chart:before{content:"\E9AC"}.fe-play:before{content:"\E9AD"}.fe-play-circle:before{content:"\E9AE"}.fe-plus:before{content:"\E9AF"}.fe-plus-circle:before{content:"\E9B0"}.fe-plus-square:before{content:"\E9B1"}.fe-pocket:before{content:"\E9B2"}.fe-power:before{content:"\E9B3"}.fe-printer:before{content:"\E9B4"}.fe-radio:before{content:"\E9B5"}.fe-refresh-ccw:before{content:"\E9B6"}.fe-refresh-cw:before{content:"\E9B7"}.fe-repeat:before{content:"\E9B8"}.fe-rewind:before{content:"\E9B9"}.fe-rotate-ccw:before{content:"\E9BA"}.fe-rotate-cw:before{content:"\E9BB"}.fe-rss:before{content:"\E9BC"}.fe-save:before{content:"\E9BD"}.fe-scissors:before{content:"\E9BE"}.fe-search:before{content:"\E9BF"}.fe-send:before{content:"\E9C0"}.fe-server:before{content:"\E9C1"}.fe-settings:before{content:"\E9C2"}.fe-share:before{content:"\E9C3"}.fe-share-2:before{content:"\E9C4"}.fe-shield:before{content:"\E9C5"}.fe-shield-off:before{content:"\E9C6"}.fe-shopping-bag:before{content:"\E9C7"}.fe-shopping-cart:before{content:"\E9C8"}.fe-shuffle:before{content:"\E9C9"}.fe-sidebar:before{content:"\E9CA"}.fe-skip-back:before{content:"\E9CB"}.fe-skip-forward:before{content:"\E9CC"}.fe-slack:before{content:"\E9CD"}.fe-slash:before{content:"\E9CE"}.fe-sliders:before{content:"\E9CF"}.fe-smartphone:before{content:"\E9D0"}.fe-speaker:before{content:"\E9D1"}.fe-square:before{content:"\E9D2"}.fe-star:before{content:"\E9D3"}.fe-stop-circle:before{content:"\E9D4"}.fe-sun:before{content:"\E9D5"}.fe-sunrise:before{content:"\E9D6"}.fe-sunset:before{content:"\E9D7"}.fe-tablet:before{content:"\E9D8"}.fe-tag:before{content:"\E9D9"}.fe-target:before{content:"\E9DA"}.fe-terminal:before{content:"\E9DB"}.fe-thermometer:before{content:"\E9DC"}.fe-thumbs-down:before{content:"\E9DD"}.fe-thumbs-up:before{content:"\E9DE"}.fe-toggle-left:before{content:"\E9DF"}.fe-toggle-right:before{content:"\E9E0"}.fe-trash:before{content:"\E9E1"}.fe-trash-2:before{content:"\E9E2"}.fe-trending-down:before{content:"\E9E3"}.fe-trending-up:before{content:"\E9E4"}.fe-triangle:before{content:"\E9E5"}.fe-truck:before{content:"\E9E6"}.fe-tv:before{content:"\E9E7"}.fe-twitter:before{content:"\E9E8"}.fe-type:before{content:"\E9E9"}.fe-umbrella:before{content:"\E9EA"}.fe-underline:before{content:"\E9EB"}.fe-unlock:before{content:"\E9EC"}.fe-upload:before{content:"\E9ED"}.fe-upload-cloud:before{content:"\E9EE"}.fe-user:before{content:"\E9EF"}.fe-user-check:before{content:"\E9F0"}.fe-user-minus:before{content:"\E9F1"}.fe-user-plus:before{content:"\E9F2"}.fe-user-x:before{content:"\E9F3"}.fe-users:before{content:"\E9F4"}.fe-video:before{content:"\E9F5"}.fe-video-off:before{content:"\E9F6"}.fe-voicemail:before{content:"\E9F7"}.fe-volume:before{content:"\E9F8"}.fe-volume-1:before{content:"\E9F9"}.fe-volume-2:before{content:"\E9FA"}.fe-volume-x:before{content:"\E9FB"}.fe-watch:before{content:"\E9FC"}.fe-wifi:before{content:"\E9FD"}.fe-wifi-off:before{content:"\E9FE"}.fe-wind:before{content:"\E9FF"}.fe-x:before{content:"\EA00"}.fe-x-circle:before{content:"\EA01"}.fe-x-square:before{content:"\EA02"}.fe-zap:before{content:"\EA03"}.fe-zap-off:before{content:"\EA04"}.fe-zoom-in:before{content:"\EA05"}.fe-zoom-out:before{content:"\EA06"} \ No newline at end of file diff --git a/onvm_web/web-build/static/css/main.006d58a7.chunk.css b/onvm_web/web-build/static/css/main.006d58a7.chunk.css deleted file mode 100644 index 9ada478bd..000000000 --- a/onvm_web/web-build/static/css/main.006d58a7.chunk.css +++ /dev/null @@ -1,2 +0,0 @@ -body{margin:0;padding:0;font-family:sans-serif}.c3 svg{font:10px sans-serif;-webkit-tap-highlight-color:transparent;font-family:Source Sans Pro,-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica Neue,Arial,sans-serif}.c3 line,.c3 path{fill:none;stroke:rgba(0,40,100,.12)}.c3 text{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;font-size:px2rem(12px)}.c3-bars path,.c3-event-rect,.c3-legend-item-tile,.c3-xgrid-focus,.c3-ygrid{shape-rendering:crispEdges}.c3-chart-arc path{stroke:#fff}.c3-chart-arc text{fill:#fff;font-size:13px}.c3-grid line{stroke:#f0f0f0}.c3-grid text{fill:#aaa}.c3-xgrid,.c3-ygrid{stroke:#e6e6e6;stroke-dasharray:2 4}.c3-text{font-size:12px}.c3-text.c3-empty{fill:grey;font-size:2em}.c3-line{stroke-width:2px}.c3-circle._expanded_{stroke-width:2px;stroke:#fff}.c3-selected-circle{fill:#fff;stroke-width:1.5px}.c3-bar{stroke-width:0}.c3-bar._expanded_{fill-opacity:1;fill-opacity:.75}.c3-target.c3-focused{opacity:1}.c3-target.c3-focused path.c3-line,.c3-target.c3-focused path.c3-step{stroke-width:2px}.c3-target.c3-defocused{opacity:.3!important}.c3-region{fill:#4682b4;fill-opacity:.1}.c3-brush .extent{fill-opacity:.1}.c3-legend-item text{fill:#545454;font-size:14px}.c3-legend-item-hidden{opacity:.15}.c3-legend-background{fill:transparent;stroke:#d3d3d3;stroke-width:0}.c3-title{font:14px sans-serif}.c3-tooltip-container{z-index:10}.c3-tooltip{border-collapse:collapse;border-spacing:0;empty-cells:show;font-size:11px;line-height:1;font-weight:700;color:#fff;border-radius:3px;background:#212529!important;white-space:nowrap}.c3-tooltip th{padding:6px;text-align:left}.c3-tooltip td{padding:4px 6px;font-weight:400}.c3-tooltip td>span{display:inline-block;width:8px;height:8px;margin-right:8px;border-radius:50%;vertical-align:initial}.c3-tooltip td.value{text-align:right}.c3-area{stroke-width:0;opacity:.1}.c3-target-filled .c3-area{opacity:1!important}.c3-chart-arcs-title{dominant-baseline:middle;font-size:1.3em}.c3-chart-arcs .c3-chart-arcs-background{fill:#e0e0e0;stroke:none}.c3-chart-arcs .c3-chart-arcs-gauge-unit{fill:#000;font-size:16px}.c3-chart-arcs .c3-chart-arcs-gauge-max,.c3-chart-arcs .c3-chart-arcs-gauge-min{fill:#777}.c3-chart-arc .c3-gauge-value{fill:#000}.c3-chart-arc.c3-target.c3-focused g path,.c3-chart-arc.c3-target g path{opacity:1}.c3-axis{fill:#9aa0ac} -/*# sourceMappingURL=main.006d58a7.chunk.css.map */ \ No newline at end of file diff --git a/onvm_web/web-build/static/css/main.3a4efa10.chunk.css b/onvm_web/web-build/static/css/main.3a4efa10.chunk.css new file mode 100644 index 000000000..26463cfd0 --- /dev/null +++ b/onvm_web/web-build/static/css/main.3a4efa10.chunk.css @@ -0,0 +1 @@ +body{margin:0;padding:0;font-family:sans-serif}.c3 svg{font:10px sans-serif;-webkit-tap-highlight-color:transparent;font-family:Source Sans Pro,-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica Neue,Arial,sans-serif}.c3 line,.c3 path{fill:none;stroke:rgba(0,40,100,.12)}.c3 text{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;font-size:px2rem(12px)}.c3-bars path,.c3-event-rect,.c3-legend-item-tile,.c3-xgrid-focus,.c3-ygrid{shape-rendering:crispEdges}.c3-chart-arc path{stroke:#fff}.c3-chart-arc text{fill:#fff;font-size:13px}.c3-grid line{stroke:#f0f0f0}.c3-grid text{fill:#aaa}.c3-xgrid,.c3-ygrid{stroke:#e6e6e6;stroke-dasharray:2 4}.c3-text{font-size:12px}.c3-text.c3-empty{fill:grey;font-size:2em}.c3-line{stroke-width:2px}.c3-circle._expanded_{stroke-width:2px;stroke:#fff}.c3-selected-circle{fill:#fff;stroke-width:1.5px}.c3-bar{stroke-width:0}.c3-bar._expanded_{fill-opacity:1;fill-opacity:.75}.c3-target.c3-focused{opacity:1}.c3-target.c3-focused path.c3-line,.c3-target.c3-focused path.c3-step{stroke-width:2px}.c3-target.c3-defocused{opacity:.3 !important}.c3-region{fill:#4682b4;fill-opacity:.1}.c3-brush .extent{fill-opacity:.1}.c3-legend-item text{fill:#545454;font-size:14px}.c3-legend-item-hidden{opacity:.15}.c3-legend-background{fill:transparent;stroke:#d3d3d3;stroke-width:0}.c3-title{font:14px sans-serif}.c3-tooltip-container{z-index:10}.c3-tooltip{border-collapse:collapse;border-spacing:0;empty-cells:show;font-size:11px;line-height:1;font-weight:700;color:#fff;border-radius:3px;background:#212529 !important;white-space:nowrap}.c3-tooltip th{padding:6px;text-align:left}.c3-tooltip td{padding:4px 6px;font-weight:400}.c3-tooltip td>span{display:inline-block;width:8px;height:8px;margin-right:8px;border-radius:50%;vertical-align:initial}.c3-tooltip td.value{text-align:right}.c3-area{stroke-width:0;opacity:.1}.c3-target-filled .c3-area{opacity:1 !important}.c3-chart-arcs-title{dominant-baseline:middle;font-size:1.3em}.c3-chart-arcs .c3-chart-arcs-background{fill:#e0e0e0;stroke:none}.c3-chart-arcs .c3-chart-arcs-gauge-unit{fill:#000;font-size:16px}.c3-chart-arcs .c3-chart-arcs-gauge-max,.c3-chart-arcs .c3-chart-arcs-gauge-min{fill:#777}.c3-chart-arc .c3-gauge-value{fill:#000}.c3-chart-arc.c3-target.c3-focused g path,.c3-chart-arc.c3-target g path{opacity:1}.c3-axis{fill:#9aa0ac} \ No newline at end of file diff --git a/onvm_web/web-build/static/js/1.34246748.chunk.js b/onvm_web/web-build/static/js/1.5af065d1.chunk.js similarity index 78% rename from onvm_web/web-build/static/js/1.34246748.chunk.js rename to onvm_web/web-build/static/js/1.5af065d1.chunk.js index d69435713..1aa754897 100644 --- a/onvm_web/web-build/static/js/1.34246748.chunk.js +++ b/onvm_web/web-build/static/js/1.5af065d1.chunk.js @@ -1,2 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[1],[function(e,t,n){"use strict";e.exports=n(24)},function(e,t,n){e.exports=n(31)()},function(e,t,n){"use strict";(function(e){n.d(t,"a",function(){return w}),n.d(t,"b",function(){return Dr}),n.d(t,"c",function(){return M}),n.d(t,"d",function(){return ai}),n.d(t,"e",function(){return ca}),n.d(t,"f",function(){return Ta}),n.d(t,"g",function(){return Ia});var r=n(0),i=n.n(r),a={profile:{icon:"user",value:"Profile",to:"/profile"},settings:{icon:"settings",value:"Settings",to:"/settings"},mail:{icon:"mail",value:"Inbox",to:"/mail"},message:{icon:"send",value:"Message",to:"/message"},help:{icon:"help-circle",value:"Need help?",to:"/help"},logout:{icon:"log-out",value:"Sign out",to:"/logout"},divider:{isDivider:!0}},o=function(e){return e.map(function(e){return"string"===typeof e?a[e]:e})};function s(e){var t=e.avatarURL,n=e.name,i=e.description,a=e.options,s=void 0===a?[]:a,l=e.optionsRootComponent,u=o(s);return Object(r.createElement)(Kr,{isNavLink:!0,triggerClassName:"pr-0 leading-none",triggerContent:Object(r.createElement)(r.Fragment,null,Object(r.createElement)(_,{imageURL:t}),Object(r.createElement)("span",{className:"ml-2 d-none d-lg-block"},Object(r.createElement)("span",{className:"text-default"},n),Object(r.createElement)("small",{className:"text-muted d-block mt-1"},i))),position:"bottom-end",arrow:!0,arrowPosition:"right",toggle:!1,itemsObject:u,itemsRootComponent:l})}var l="undefined"!==typeof window?window:"undefined"!==typeof e?e:"undefined"!==typeof self?self:{};function u(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function c(e,t){return e(t={exports:{}},t.exports),t.exports}var d=c(function(e){!function(){var t={}.hasOwnProperty;function n(){for(var e=[],r=0;r=0||Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n},b=function(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!==typeof t&&"function"!==typeof t?e:t};function x(e){var t=e.className,n=e.children,i=e.stacked,a=d({"avatar-list":!0,"avatar-list-stacked":i},t);return Object(r.createElement)("div",{className:a},n)}function _(e){var t,n=e.className,i=e.children,a=e.imageURL,o=e.style,s=e.size,l=void 0===s?"":s,u=e.status,c=e.placeholder,f=e.icon,h=e.color,p=void 0===h?"":h,m=e.onClick,v=e.onMouseEnter,y=e.onMouseLeave,b=e.onPointerEnter,x=e.onPointerLeave,_=d((g(t={avatar:!0},"avatar-"+l,!!l),g(t,"avatar-placeholder",c),g(t,"avatar-"+p,!!p),t),n);return Object(r.createElement)("span",{className:_,style:a?Object.assign({backgroundImage:"url("+a+")"},o):o,onClick:m,onMouseEnter:v,onMouseLeave:y,onPointerEnter:b,onPointerLeave:x},f&&Object(r.createElement)(L,{name:f}),u&&Object(r.createElement)("span",{className:"avatar-status bg-"+u}),i)}function w(e){var t=e.className,n=e.children,i=e.color,a=void 0===i?"primary":i,o=d(g({badge:!0},"badge-"+a,a),t);return Object(r.createElement)("span",{className:o},n)}function S(e){var t=e.className,n=e.children,i=e.backgroundURL,a=void 0===i?"":i,o=d("card-header",t);return Object(r.createElement)("div",{className:o,style:a?Object.assign({backgroundImage:"url("+a+")"}):null},n)}function T(e){var t=e.className,n=e.children,i=e.RootComponent,a=d("card-title",t),o=i||"h3";return Object(r.createElement)(o,{className:a},n)}function E(e){var t=e.className,n=e.children,i=d("card-body",t);return Object(r.createElement)("div",{className:i},n)}function C(e){var t=e.className,n=e.children,i=d("card-options",t);return Object(r.createElement)("div",{className:i},n)}function O(e){var t=e.className,n=(e.children,e.icon),i=e.type,a=e.onClick,o=d({"card-options-collapse":"collapse"===i,"card-options-remove":"close"===i,"card-options-fullscreen":"fullscreen"===i},t),s=function(){switch(i){case"collapse":return"card-collapse";case"close":case"fullscreen":return"card-remove";default:return""}}(),l=function(){if(n)return n;switch(i){case"collapse":return"chevron-up";case"close":return"x";case"fullscreen":return"maximize";default:return""}}();return Object(r.createElement)("a",{className:o,"data-toggle":s,onClick:a},Object(r.createElement)(L,{name:l}))}function P(e){var t,n=e.className,i=e.children,a=e.color,o=e.side,s=d((g(t={"card-status":!0},"bg-"+a,!0),g(t,"card-status-left",o),t),n);return Object(r.createElement)("div",{className:s},i)}function A(e){var t=e.className,n=e.children,i=e.color,a=d("card-alert alert alert-"+i+" mb-0",t);return Object(r.createElement)("div",{className:a},n)}function k(e){var t=e.className,n=e.children,i=d("card-footer",t);return Object(r.createElement)("div",{className:i},n)}function N(e){var t=e.className,n=e.children,i=e.placeholder,a=d("card-map",{"card-map-placeholder":i},t);return Object(r.createElement)("div",{className:a,style:i&&{backgroundImage:"url("+i+")"}},n)}(function(e){function t(){var e,n,r;h(this,t);for(var i=arguments.length,a=Array(i),o=0;o0?le:se)(e)},ce=Math.min,de=Math.max,fe=Math.min,he=R["__core-js_shared__"]||(R["__core-js_shared__"]={}),pe=function(e){return he[e]||(he[e]={})},ge=0,me=Math.random(),ve=function(e){return"Symbol(".concat(void 0===e?"":e,")_",(++ge+me).toString(36))},ye=pe("keys"),be=function(e){return ye[e]||(ye[e]=ve(e))},xe=(ee=!1,function(e,t,n){var r,i,a=oe(e),o=(r=a.length)>0?ce(ue(r),9007199254740991):0,s=function(e,t){return(e=ue(e))<0?de(e+t,0):fe(e,t)}(n,o);if(ee&&t!=t){for(;o>s;)if((i=a[s++])!=i)return!0}else for(;o>s;s++)if((ee||s in a)&&a[s]===t)return ee||s||0;return!ee&&-1}),_e=be("IE_PROTO"),we=function(e,t){var n,r=oe(e),i=0,a=[];for(n in r)n!=_e&&Z(r,n)&&a.push(n);for(;t.length>i;)Z(r,n=t[i++])&&(~xe(a,n)||a.push(n));return a},Se="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(","),Te=Object.keys||function(e){return we(e,Se)},Ee={f:Object.getOwnPropertySymbols},Ce={f:{}.propertyIsEnumerable},Oe=function(e){return Object(ae(e))},Pe=Object.assign,Ae=!Pe||D(function(){var e={},t={},n=Symbol(),r="abcdefghijklmnopqrst";return e[n]=7,r.split("").forEach(function(e){t[e]=e}),7!=Pe({},e)[n]||Object.keys(Pe({},t)).join("")!=r})?function(e,t){for(var n=Oe(e),r=arguments.length,i=1,a=Ee.f,o=Ce.f;r>i;)for(var s,l=ie(arguments[i++]),u=a?Te(l).concat(a(l)):Te(l),c=u.length,d=0;c>d;)o.call(l,s=u[d++])&&(n[s]=l[s]);return n}:Pe;te(te.S+te.F,"Object",{assign:Ae});var ke=I.Object.assign,Ne=c(function(e){e.exports={default:ke,__esModule:!0}});u(Ne);var Me=u(c(function(e,t){t.__esModule=!0;var n,r=(n=Ne)&&n.__esModule?n:{default:n};t.default=r.default||function(e){for(var t=1;ta;)q.f(e,n=r[a++],t[n]);return e},Xe=R.document,Ye=Xe&&Xe.documentElement,We=be("IE_PROTO"),qe=function(){},Qe=function(){var e,t=U("iframe"),n=Se.length;for(t.style.display="none",Ye.appendChild(t),t.src="javascript:",(e=t.contentWindow.document).open(),e.write("